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