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: 1.13 $
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    
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.ELIST: // a,b,c
094                    case GroovyTokenTypes.PARAMETERS: // a,b,c
095                    case GroovyTokenTypes.STRING_CONSTRUCTOR: // "foo${bar}wibble"
096                        accept_v_FirstChild_v_SecondChild_v___LastChild_v(t);
097                        break;
098    
099                    case GroovyTokenTypes.INDEX_OP:
100                        accept_FirstChild_v_SecondChild_v(t);
101                        break;
102    
103                    case GroovyTokenTypes.EXPR:
104                    case GroovyTokenTypes.IMPORT:
105                    case GroovyTokenTypes.PACKAGE_DEF:
106                    case GroovyTokenTypes.VARIABLE_DEF:
107                    case GroovyTokenTypes.METHOD_DEF:
108                    case GroovyTokenTypes.OBJBLOCK: //class Foo {def bar()}  <-- this block
109                    case GroovyTokenTypes.PARAMETER_DEF:
110                    case GroovyTokenTypes.SLIST: // list of expressions, variable defs etc
111                        accept_v_AllChildren_v(t);
112                        break;
113    
114                    case GroovyTokenTypes.ASSIGN: // a=b
115                    case GroovyTokenTypes.EQUAL: // a==b
116                    case GroovyTokenTypes.NOT_EQUAL:
117                        if (t.childAt(1) != null) {
118                            accept_FirstChild_v_RestOfTheChildren(t);
119                        } else {
120                            accept_v_FirstChild_v_RestOfTheChildren(t);
121                        }
122                        break;
123    
124                    case GroovyTokenTypes.CLASS_DEF: // class Foo...
125                    case GroovyTokenTypes.CTOR_IDENT: // private Foo() {...
126                    case GroovyTokenTypes.DOT: // foo.bar
127                    case GroovyTokenTypes.GT: // a > b
128                    case GroovyTokenTypes.LABELED_ARG: // myMethod(name:"Jez")
129                    case GroovyTokenTypes.LAND: // true && false
130                    case GroovyTokenTypes.LT: // a < b
131                    case GroovyTokenTypes.MEMBER_POINTER: // this.&foo()
132                    case GroovyTokenTypes.MINUS:
133                    case GroovyTokenTypes.PLUS:
134                    case GroovyTokenTypes.RANGE_EXCLUSIVE:
135                    case GroovyTokenTypes.RANGE_INCLUSIVE:
136                    case GroovyTokenTypes.STAR: // a * b   or    import foo.*
137                        accept_FirstChild_v_RestOfTheChildren(t);
138                        break;
139    
140                    case GroovyTokenTypes.METHOD_CALL:
141                        if (t.getNumberOfChildren() == 2 && t.childAt(1) != null && t.childAt(1).getType() == GroovyTokenTypes.CLOSED_BLOCK ) {
142                            // myMethod {...
143                            accept_FirstChild_v_SecondChild(t);
144                        } else {
145                            GroovySourceAST lastChild = t.childAt(t.getNumberOfChildren() -1);
146                            if (lastChild != null && lastChild.getType() == GroovyTokenTypes.CLOSED_BLOCK) {
147                                // myMethod(a,b) {...
148                                accept_FirstChild_v_RestOfTheChildren_v_LastChild(t);
149                            } else {
150                                // myMethod(a,b)
151                                accept_FirstChild_v_RestOfTheChildren_v(t);
152                            }
153                        }
154                        break;
155    
156                    case GroovyTokenTypes.LITERAL_while:
157                    case GroovyTokenTypes.TYPECAST: // (String)itr.next()
158                        accept_v_FirstChildsFirstChild_v_RestOfTheChildren(t);
159                        break;
160    
161                    case GroovyTokenTypes.LITERAL_if: // if (grandchild) {child1} else {child2} ...
162                        accept_v_FirstChildsFirstChild_v_Child2_Child3_v_Child4_v___v_LastChild(t);
163                        break;
164    
165                    case GroovyTokenTypes.CLOSED_BLOCK: // [1,2,3].each {foo(it)}  <-- Closure
166                        if (t.childAt(0) != null && t.childAt(0).getType() == GroovyTokenTypes.IMPLICIT_PARAMETERS) {
167                            accept_v_AllChildren_v(t);
168                        } else {
169                            accept_v_FirstChild_v_RestOfTheChildren_v(t);
170                        }
171                        break;
172    
173                    case GroovyTokenTypes.FOR_IN_ITERABLE:
174                    case GroovyTokenTypes.LITERAL_for:
175                    case GroovyTokenTypes.LITERAL_new:
176                    case GroovyTokenTypes.LITERAL_switch:
177                        accept_v_FirstChild_v_RestOfTheChildren_v(t);
178                        break;
179    
180                    case GroovyTokenTypes.LITERAL_catch:
181                    case GroovyTokenTypes.LITERAL_try:
182                        accept_v_FirstChild_v_RestOfTheChildren(t);
183                        break;
184    
185                    default:
186                        accept_v_FirstChild_v(t);
187                        break;
188                }
189            }
190        }
191    }