001    /**
002     *
003     * Copyright 2005 Jeremy Rayner
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     * http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     *
017     **/
018    package org.codehaus.groovy.antlr.treewalker;
019    
020    import java.util.ArrayList;
021    import java.util.Collections;
022    
023    import org.codehaus.groovy.antlr.GroovySourceAST;
024    import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
025    
026    /**
027     * A treewalker for the antlr generated AST that attempts to visit the
028     * AST nodes in the order needed to generate valid groovy source code.
029     *
030     * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
031     * @version $Revision: 4538 $
032     */
033    public class SourceCodeTraversal extends TraversalHelper {
034        /**
035         * Constructs a treewalker for the antlr generated AST that attempts to visit the
036         * AST nodes in the order needed to generate valid groovy source code.
037         * @param visitor the visitor implementation to call for each AST node.
038         */
039        public SourceCodeTraversal(Visitor visitor) {
040            super(visitor);
041        }
042    
043        /**
044         * gather, sort and process all unvisited nodes
045         * @param t the AST to process
046         */
047        public void setUp(GroovySourceAST t) {
048            super.setUp(t);
049            
050            // gather and sort all unvisited AST nodes
051            unvisitedNodes = new ArrayList();
052            traverse((GroovySourceAST)t);
053            Collections.sort(unvisitedNodes);
054        }
055    
056        /**
057         * traverse an AST node
058         * @param t the AST node to traverse
059         */
060        private void traverse(GroovySourceAST t) {
061            if (t == null) { return; }
062            if (unvisitedNodes != null) {
063               unvisitedNodes.add(t);
064            }
065            GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
066            if (child != null) {
067                traverse(child);
068            }
069            GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
070            if (sibling != null) {
071                traverse(sibling);
072            }
073        }
074    
075        protected void accept(GroovySourceAST currentNode) {
076            if (currentNode != null && unvisitedNodes != null && unvisitedNodes.size() > 0) {
077                GroovySourceAST t = currentNode;
078    
079                if (!(unvisitedNodes.contains(currentNode))) {
080                    return;
081                }
082                push(t);
083                switch (t.getType()) {
084                    case GroovyTokenTypes.QUESTION: // expr?foo:bar
085                        accept_FirstChild_v_SecondChild_v_ThirdChild_v(t);
086                        break;
087    
088                    case GroovyTokenTypes.CASE_GROUP: //
089                    case GroovyTokenTypes.LITERAL_instanceof: // foo instanceof MyType
090                        accept_FirstChild_v_SecondChildsChildren_v(t);
091                        break;
092    
093                    case GroovyTokenTypes.ANNOTATION:
094                        accept_v_FirstChild_2ndv_SecondChild_v___LastChild_v(t);
095                        break;
096    
097                    case GroovyTokenTypes.ELIST: // a,b,c
098                    case GroovyTokenTypes.PARAMETERS: // a,b,c
099                    case GroovyTokenTypes.TYPE_ARGUMENTS: // <String, Object>
100                    case GroovyTokenTypes.STRING_CONSTRUCTOR: // "foo${bar}wibble"
101                    case GroovyTokenTypes.TYPE_PARAMETER: // class Foo<T extends F>
102                    case GroovyTokenTypes.TYPE_PARAMETERS: // class Foo<T>
103                    case GroovyTokenTypes.TYPE_UPPER_BOUNDS: // class Foo<T extends F>
104                        accept_v_FirstChild_v_SecondChild_v___LastChild_v(t);
105                        // todo : confirm that TYPE_LOWER_BOUNDS does not have multiple children
106                        break;
107    
108                    case GroovyTokenTypes.VARIABLE_PARAMETER_DEF: // void f(String ... others) {}
109                        accept_v_FirstChild_SecondChild_v_ThirdChild_v(t);
110                        break;
111    
112                    case GroovyTokenTypes.INDEX_OP:
113                        accept_FirstChild_v_SecondChild_v(t);
114                        break;
115    
116                    case GroovyTokenTypes.ENUM_CONSTANT_DEF: // enum Foo(THESE,ARE,THEY)
117                    case GroovyTokenTypes.EXPR:
118                    case GroovyTokenTypes.IMPORT:
119                    case GroovyTokenTypes.VARIABLE_DEF:
120                    case GroovyTokenTypes.METHOD_DEF:
121                    case GroovyTokenTypes.OBJBLOCK: //class Foo {def bar()}  <-- this block
122                    case GroovyTokenTypes.PARAMETER_DEF: // void f(String me) {}
123                    case GroovyTokenTypes.SLIST: // list of expressions, variable defs etc
124                        accept_v_AllChildren_v(t);
125                        break;
126    
127                    case GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR: // @Blue(foo=123)
128                    case GroovyTokenTypes.ASSIGN: // a = b
129                    case GroovyTokenTypes.BAND_ASSIGN: // a &= b
130                    case GroovyTokenTypes.BOR_ASSIGN: // a |= b
131                    case GroovyTokenTypes.BSR_ASSIGN: // a >>>= b
132                    case GroovyTokenTypes.BXOR_ASSIGN: // a ^= b
133                    case GroovyTokenTypes.COMPARE_TO: // a <=> b
134                    case GroovyTokenTypes.DIV_ASSIGN: // a /= b
135                    case GroovyTokenTypes.EQUAL: // a == b
136                    case GroovyTokenTypes.MINUS_ASSIGN: // a -= b
137                    case GroovyTokenTypes.MOD_ASSIGN: // a %= b
138                    case GroovyTokenTypes.NOT_EQUAL: // a != b
139                    case GroovyTokenTypes.PLUS_ASSIGN: // a += b
140                    case GroovyTokenTypes.REGEX_FIND: // a =~ b
141                    case GroovyTokenTypes.REGEX_MATCH: // a ==~ b
142                    case GroovyTokenTypes.SL_ASSIGN: // a <<= b
143                    case GroovyTokenTypes.SR_ASSIGN: // a >>= b
144                    case GroovyTokenTypes.STAR_ASSIGN: // a *= b
145                    case GroovyTokenTypes.STAR_STAR_ASSIGN: // x **= 3
146                        if (t.childAt(1) != null) {
147                            accept_FirstChild_v_RestOfTheChildren(t);
148                        } else {
149                            accept_v_FirstChild_v_RestOfTheChildren(t);
150                        }
151                        break;
152    
153                    case GroovyTokenTypes.ANNOTATION_FIELD_DEF: // @interface Foo{ int bar()...
154                        accept_FirstSecondAndThirdChild_v_v_ForthChild(t);
155                        break;
156                        
157                    case GroovyTokenTypes.ANNOTATION_DEF: // @interface Foo...
158                    case GroovyTokenTypes.BAND: // 1 & 2
159                    case GroovyTokenTypes.BOR: // 1 | 2
160                    case GroovyTokenTypes.BSR: // 1 >>> 2
161                    case GroovyTokenTypes.BXOR: // 1 ^ 2
162                    case GroovyTokenTypes.CLASS_DEF: // class Foo...
163                    case GroovyTokenTypes.CTOR_IDENT: // private Foo() {...
164                    case GroovyTokenTypes.DIV: //  3/4
165                    case GroovyTokenTypes.DOT: // foo.bar
166                    case GroovyTokenTypes.ENUM_DEF: // enum Foo...
167                    case GroovyTokenTypes.GE: // a >= b
168                    case GroovyTokenTypes.GT: // a > b
169                    case GroovyTokenTypes.INTERFACE_DEF: // interface Foo...
170                    case GroovyTokenTypes.LABELED_ARG: // myMethod(name:"Jez")
171                    case GroovyTokenTypes.LABELED_STAT: // foo:x=1                          
172                    case GroovyTokenTypes.LAND: // true && false
173                    case GroovyTokenTypes.LE: // a <= b
174                    case GroovyTokenTypes.LITERAL_as: // foo as Bar
175                    case GroovyTokenTypes.LITERAL_in: // if (i in myList) ...
176                    case GroovyTokenTypes.LOR: // true && false
177                    case GroovyTokenTypes.LT: // a < b
178                    case GroovyTokenTypes.MEMBER_POINTER: // this.&foo()
179                    case GroovyTokenTypes.MOD: //  4 % 3
180                    case GroovyTokenTypes.MINUS: // 1 - 1
181                    case GroovyTokenTypes.OPTIONAL_DOT: // foo?.bar
182                    case GroovyTokenTypes.PACKAGE_DEF:
183                    case GroovyTokenTypes.PLUS: // 1 + 1
184                    case GroovyTokenTypes.RANGE_EXCLUSIVE: // [1..<10]
185                    case GroovyTokenTypes.RANGE_INCLUSIVE: // [1..10]
186                    case GroovyTokenTypes.SL: // a << b
187                    case GroovyTokenTypes.SR: // a >> b
188                    case GroovyTokenTypes.STAR: // a * b   or    import foo.*
189                    case GroovyTokenTypes.STAR_STAR: // x ** 3
190                        accept_FirstChild_v_RestOfTheChildren(t);
191                        break;
192    
193                    case GroovyTokenTypes.CTOR_CALL:
194                    case GroovyTokenTypes.METHOD_CALL:
195                        if (t.getNumberOfChildren() == 2 && t.childAt(1) != null && t.childAt(1).getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
196                            // myMethod {...
197                            accept_FirstChild_v_SecondChild(t);
198                        } else {
199                            GroovySourceAST lastChild = t.childAt(t.getNumberOfChildren() -1);
200                            if (lastChild != null && lastChild.getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
201                                // myMethod(a,b) {...
202                                accept_FirstChild_v_RestOfTheChildren_v_LastChild(t);
203                            } else {
204                                // myMethod(a,b)
205                                accept_FirstChild_v_RestOfTheChildren_v(t);
206                            }
207                        }
208                        break;
209    
210                    case GroovyTokenTypes.LITERAL_while:
211                    case GroovyTokenTypes.LITERAL_with:
212                    case GroovyTokenTypes.TYPECAST: // (String)itr.next()
213                        accept_v_FirstChildsFirstChild_v_RestOfTheChildren(t);
214                        break;
215    
216                    case GroovyTokenTypes.LITERAL_if: // if (grandchild) {child1} else {child2} ...
217                        accept_v_FirstChildsFirstChild_v_Child2_Child3_v_Child4_v___v_LastChild(t);
218                        break;
219    
220                    case GroovyTokenTypes.CLOSABLE_BLOCK: // [1,2,3].each {foo(it)}  <-- Closure
221                        if (t.childAt(0) != null && t.childAt(0).getType() == GroovyTokenTypes.IMPLICIT_PARAMETERS) {
222                            accept_v_AllChildren_v(t);
223                        } else {
224                            accept_v_FirstChild_v_RestOfTheChildren_v(t);
225                        }
226                        break;
227    
228                    case GroovyTokenTypes.FOR_IN_ITERABLE:
229                    case GroovyTokenTypes.LITERAL_for:
230                    case GroovyTokenTypes.LITERAL_new:
231                    case GroovyTokenTypes.LITERAL_switch:
232                        accept_v_FirstChild_v_RestOfTheChildren_v(t);
233                        break;
234     
235                    case GroovyTokenTypes.ANNOTATIONS: // just like modifiers but for package/enum declarations
236                    case GroovyTokenTypes.LITERAL_assert:
237                    case GroovyTokenTypes.LITERAL_catch:
238                    case GroovyTokenTypes.LITERAL_synchronized:
239                    case GroovyTokenTypes.LITERAL_try:
240                    case GroovyTokenTypes.MODIFIERS:
241                        accept_v_FirstChild_v_RestOfTheChildren(t);
242                        break;
243    
244                    default:
245                        accept_v_FirstChild_v(t);
246                        break;
247                }
248                pop();
249            }
250        }
251    }