001    /**
002     *
003     * Copyright 2004 James Strachan
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.syntax;
019    
020    import org.codehaus.groovy.ast.ModuleNode;
021    import org.codehaus.groovy.control.SourceUnit;
022    
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    
028    /**
029     * A common base class of AST helper methods which can be shared across the classic and new parsers
030     *
031     * @author James Strachan
032     * @author Bob McWhirter
033     * @author Sam Pullara
034     * @author Chris Poirier
035     * @version $Revision: 1.9 $
036     */
037    public class ASTHelper {
038    
039        private static final String[] EMPTY_STRING_ARRAY = new String[0];
040    
041        /** The SourceUnit controlling us */
042        private SourceUnit controller;
043    
044        /** Our ClassLoader, which provides information on external types */
045        private ClassLoader classLoader;
046    
047        /** Our imports, simple name => fully qualified name */
048        private Map imports;
049        protected ModuleNode output;
050    
051        /** The package name in which the module sits */
052        private String packageName;   //
053    
054        // TODO should this really be static???
055        protected static HashMap resolutions = new HashMap();  // cleared on build(), to be safe
056    
057        private static String NOT_RESOLVED = new String();
058    
059        /** temporarily store the class names that the current modulenode contains */
060        private List newClasses = new ArrayList();
061    
062        public ASTHelper(SourceUnit controller, ClassLoader classLoader) {
063            this();
064            this.controller = controller;
065            this.classLoader = classLoader;
066        }
067    
068        public ASTHelper() {
069            imports = new HashMap();
070        }
071    
072        public String getPackageName() {
073            return packageName;
074        }
075    
076        public void setPackageName(String packageName) {
077            this.packageName = packageName;
078            if (packageName!=null && packageName.length()>0){
079                packageName+='.';
080            }
081            output.setPackageName(packageName);
082        }
083    
084    
085        /**
086         * Returns our class loader (as supplied on construction).
087         */
088        public ClassLoader getClassLoader() {
089            return classLoader;
090        }
091    
092        public void setClassLoader(ClassLoader classLoader) {
093            this.classLoader = classLoader;
094        }
095    
096        public SourceUnit getController() {
097            return controller;
098        }
099    
100        public void setController(SourceUnit controller) {
101            this.controller = controller;
102        }
103        
104        /**
105         * Returns a fully qualified name for any given potential type
106         * name.  Returns null if no qualified name could be determined.
107         */
108    /*    protected String resolveName(String name, boolean safe) {
109            //
110            // Use our cache of resolutions, if possible
111    
112            String resolution = (String) resolutions.get(name);
113            if (NOT_RESOLVED.equals(resolution)) {
114                return (safe ? name : null);
115            }
116            else if (resolution != null) {
117                return (String) resolution;
118            }
119    
120            try {
121                getClassLoader().loadClass(name);
122                resolutions.put(name,name);
123                return name;
124            } catch (ClassNotFoundException cnfe){
125                if (cnfe.getCause() instanceof MultipleCompilationErrorsException) {
126                    MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) cnfe.getCause();
127                    controller.getErrorCollector().addCollectorContents(mcee.getErrorCollector());
128                    resolutions.put(name,name);
129                    return name;
130                }
131            } catch (NoClassDefFoundError ncdfe) {
132                //fall through
133            }
134    
135            do {
136                //
137                // If the type name contains a ".", it's probably fully
138                // qualified, and we don't take it to verification here.
139    
140                if (name.indexOf(".") >= 0) {
141                    resolution = name;
142                    break;                                            // <<< FLOW CONTROL <<<<<<<<<
143                }
144    
145    
146                //
147                // Otherwise, we'll need the scalar type for checking, and
148                // the postfix for reassembly.
149    
150                String scalar = name, postfix = "";
151                while (scalar.endsWith("[]")) {
152                    scalar = scalar.substring(0, scalar.length() - 2);
153                    postfix += "[]";
154                }
155    
156    
157                //
158                // Primitive types are all valid...
159    
160                if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) {
161                    resolution = name;
162                    break;                                            // <<< FLOW CONTROL <<<<<<<<<
163                }
164    
165    
166                //
167                // Next, check our imports and return the qualified name,
168                // if available.
169    
170                if (this.imports.containsKey(scalar)) {
171                    resolution = ((String) this.imports.get(scalar)) + postfix;
172                    break;                                            // <<< FLOW CONTROL <<<<<<<<<
173                }
174    
175    
176                //
177                // Next, see if our class loader can resolve it in the current package.
178    
179                if (packageName != null && packageName.length() > 0) {
180                    try {
181                        getClassLoader().loadClass(dot(packageName, scalar));
182                        resolution = dot(packageName, name);
183    
184                        break;                                        // <<< FLOW CONTROL <<<<<<<<<
185                    } catch (ClassNotFoundException cnfe){
186                        if (cnfe.getCause() instanceof CompilationFailedException) {
187                            resolution = dot(packageName, name);
188                            break;
189                        }
190                    } catch (NoClassDefFoundError ncdfe) {
191                        //fall through
192                    }
193                }
194    
195                // search the package imports path
196                List packageImports = output.getImportPackages();
197                for (int i = 0; i < packageImports.size(); i++) {
198                    String pack = (String) packageImports.get(i);
199                    String clsName = pack + name;
200                    try {
201                        getClassLoader().loadClass(clsName);
202                        resolution = clsName;
203                        break;
204                    } catch (ClassNotFoundException cnfe){
205                        if (cnfe.getCause() instanceof CompilationFailedException) {
206                            resolution = clsName;
207                            break;
208                        }
209                    } catch (NoClassDefFoundError ncdfe) {
210                        //fall through
211                    }
212                }
213                if (resolution != null) {
214                    break;
215                }
216    
217                //
218                // Last chance, check the default imports.
219    
220                for (int i = 0; i < DEFAULT_IMPORTS.length; i++) {
221                    String qualified = DEFAULT_IMPORTS[i] + scalar;
222                    try {
223                        getClassLoader().loadClass(qualified);
224    
225                        resolution = qualified + postfix;
226                        break;                                        // <<< FLOW CONTROL <<<<<<<<<
227                    } catch (ClassNotFoundException cnfe){
228                        if (cnfe.getCause() instanceof CompilationFailedException) {
229                            resolution = qualified + postfix;
230                            break;
231                        }
232                    } catch (NoClassDefFoundError ncdfee) {
233                        // fall through
234                    }
235                }
236    
237            }
238            while (false);
239    
240    
241            //
242            // Cache the solution and return it
243    
244            if (resolution == null) {
245                resolutions.put(name, NOT_RESOLVED);
246                return (safe ? name : null);
247            }
248            else {
249                resolutions.put(name, resolution);
250                return resolution;
251            }
252        }
253    */
254        
255        /**
256         * Returns two names joined by a dot.  If the base name is
257         * empty, returns the name unchanged.
258         */
259        public static String dot(String base, String name) {
260            if (base != null && base.length() > 0) {
261                return base + "." + name;
262            }
263    
264            return name;
265        }
266    
267        protected void makeModule() {
268            this.newClasses.clear();
269            this.output = new ModuleNode(controller);
270            resolutions.clear();
271        }
272    
273        /**
274         * A synonym for <code>dot( base, "" )</code>.
275         */
276        protected String dot(String base) {
277            return dot(base, "");
278        }
279    
280        /*protected String resolveNewClassOrName(String name, boolean safe) {
281            if (this.newClasses.contains(name)) {
282                return dot(packageName, name);
283            }
284            else {
285                return resolveName(name, safe);
286            }
287        }*/
288    
289        protected void addNewClassName(String name) {
290            this.newClasses.add(name);
291        }
292    
293        protected void importClass(String importPackage, String name, String as) {
294            if (as==null) as=name;
295    
296            name = dot( importPackage, name );
297    
298            output.addImport( as, name ); 
299            imports.put( as, name );
300        }
301    
302        protected void importPackageWithStar(String importPackage) {
303            String[] classes = output.addImportPackage( dot(importPackage) );
304            for( int i = 0; i < classes.length; i++ )
305            {
306                imports.put( classes[i], dot(importPackage, classes[i]) );
307            }
308        }
309    }