001    package org.extendj.ast;
002    
003    import java.util.HashSet;
004    import java.io.File;
005    import java.util.Set;
006    import java.util.Collections;
007    import java.util.Collection;
008    import java.util.ArrayList;
009    import beaver.*;
010    import java.util.*;
011    import java.io.ByteArrayOutputStream;
012    import java.io.PrintStream;
013    import java.lang.reflect.InvocationTargetException;
014    import java.lang.reflect.Method;
015    import org.jastadd.util.*;
016    import java.util.zip.*;
017    import java.io.*;
018    import org.jastadd.util.PrettyPrintable;
019    import org.jastadd.util.PrettyPrinter;
020    import java.io.FileNotFoundException;
021    import java.io.BufferedInputStream;
022    import java.io.DataInputStream;
023    /**
024     * @ast class
025     * @aspect ClassPath
026     * @declaredat /home/jesper/git/extendj/java4/frontend/ClassPath.jrag:131
027     */
028    public class ClassPath extends java.lang.Object {
029      
030    
031        /**
032         * Tracks all currently available packages in the program classpath.
033         */
034        private Set<String> packages = Collections.newSetFromMap(new HashMap<String,Boolean>());
035    
036      
037    
038        private boolean pathsInitialized = false;
039    
040      
041        private ArrayList<PathPart> classPath = new ArrayList<PathPart>();
042    
043      
044        private ArrayList<PathPart> sourcePath = new ArrayList<PathPart>();
045    
046      
047    
048        private final Program program;
049    
050      
051    
052        public ClassPath(Program program) {
053          this.program = program;
054        }
055    
056      
057    
058        /**
059         * Used to make the classpath empty, in case you want more control
060         * over the classpath initialization. Usually you would use
061         * addClassPath to manually setup the classpath after this.
062         */
063        public synchronized void initEmptyPaths() {
064          pathsInitialized = true;
065        }
066    
067      
068    
069        /**
070         * Set up the classpaths (standard + boot classpath).
071         */
072        private synchronized void initPaths() {
073          if (pathsInitialized) {
074            return;
075          }
076          pathsInitialized = true;
077    
078          ArrayList<String> classPaths = new ArrayList<String>();
079          ArrayList<String> sourcePaths = new ArrayList<String>();
080    
081          String[] bootclasspaths;
082          if (program.options().hasValueForOption("-bootclasspath")) {
083            bootclasspaths = program.options().getValueForOption("-bootclasspath").split(File.pathSeparator);
084          } else {
085            bootclasspaths = System.getProperty("sun.boot.class.path").split(File.pathSeparator);
086          }
087          for (int i = 0; i < bootclasspaths.length; i++) {
088            classPaths.add(bootclasspaths[i]);
089          }
090    
091          String[] extdirs;
092          if (program.options().hasValueForOption("-extdirs")) {
093            extdirs = program.options().getValueForOption("-extdirs").split(File.pathSeparator);
094          } else {
095            extdirs = System.getProperty("java.ext.dirs").split(File.pathSeparator);
096          }
097          for (int i = 0; i < extdirs.length; i++) {
098            classPaths.add(extdirs[i]);
099          }
100    
101          String[] userClasses = null;
102          if (program.options().hasValueForOption("-classpath")) {
103            userClasses = program.options().getValueForOption("-classpath").split(File.pathSeparator);
104          } else if (program.options().hasValueForOption("-cp")) {
105            userClasses = program.options().getValueForOption("-cp").split(File.pathSeparator);
106          } else {
107            userClasses = new String[] { "." };
108          }
109          if (!program.options().hasValueForOption("-sourcepath")) {
110            for (int i = 0; i < userClasses.length; i++) {
111              classPaths.add(userClasses[i]);
112              sourcePaths.add(userClasses[i]);
113            }
114          } else {
115            for (int i = 0; i < userClasses.length; i++) {
116              classPaths.add(userClasses[i]);
117            }
118            userClasses = program.options().getValueForOption("-sourcepath").split(File.pathSeparator);
119            for (int i = 0; i < userClasses.length; i++) {
120              sourcePaths.add(userClasses[i]);
121            }
122          }
123    
124          for (String path: classPaths) {
125            PathPart part = PathPart.createClassPath(path);
126            if (part != null) {
127              addClassPath(part);
128            } else if (program.options().verbose()) {
129              System.out.println("Warning: Could not use " + path + " as class path");
130            }
131          }
132          for (String path: sourcePaths) {
133            PathPart part = PathPart.createSourcePath(path);
134            if (part != null) {
135              addSourcePath(part);
136            } else if(program.options().verbose()) {
137              System.out.println("Warning: Could not use " + path + " as source path");
138            }
139          }
140        }
141    
142      
143    
144        /**
145         * Get the input stream for a compilation unit specified using a canonical
146         * name. This is used by the bytecode reader to load nested types.
147         * @param name The canonical name of the compilation unit.
148         */
149        public synchronized InputStream getInputStream(String name) {
150          try {
151            for (Iterator iter = classPath.iterator(); iter.hasNext(); ) {
152              PathPart part = (PathPart) iter.next();
153              ClassSource source = part.findSource(name);
154              if (source != ClassSource.NONE) {
155                return source.openInputStream();
156              }
157            }
158          } catch(IOException e) {
159          }
160          throw new Error("Could not find nested type " + name);
161        }
162    
163      
164    
165        /**
166         * Load a compilation unit from disk based on a classname. A class file is parsed if one exists
167         * matching the classname that is not older than a corresponding source file, otherwise the
168         * source file is selected.
169         * <p>
170         * This method is called by the LibCompilationUnit NTA.  We rely on the result of this method
171         * being cached because it will return a newly parsed compilation unit each time it is called.
172         *
173         * @return the loaded compilation unit, or the provided default compilation unit if no matching
174         * compilation unit was found.
175         */
176        public synchronized CompilationUnit getCompilationUnit(String typeName,
177            CompilationUnit defaultCompilationUnit) {
178          try {
179            initPaths();
180            ClassSource sourcePart = ClassSource.NONE;
181            ClassSource classPart = ClassSource.NONE;
182            for (PathPart part: sourcePath) {
183              sourcePart = part.findSource(typeName);
184              if (sourcePart != ClassSource.NONE) {
185                break;
186              }
187            }
188            for (PathPart part: classPath) {
189              classPart = part.findSource(typeName);
190              if (classPart != ClassSource.NONE) {
191                break;
192              }
193            }
194    
195            if (sourcePart != ClassSource.NONE && (classPart == ClassSource.NONE ||
196                  classPart.lastModified() < sourcePart.lastModified())) {
197              CompilationUnit unit = sourcePart.parseCompilationUnit(program);
198              int index = typeName.lastIndexOf('.');
199              if (index == -1) {
200                return unit;
201              }
202              String pkgName = typeName.substring(0, index);
203              if (pkgName.equals(unit.getPackageDecl())) {
204                return unit;
205              }
206            }
207            if (classPart != ClassSource.NONE) {
208              CompilationUnit unit = classPart.parseCompilationUnit(program);
209              int index = typeName.lastIndexOf('.');
210              if (index == -1) {
211                return unit;
212              }
213              String pkgName = typeName.substring(0, index);
214              if (pkgName.equals(unit.getPackageDecl())) {
215                return unit;
216              }
217            }
218            return defaultCompilationUnit;
219          } catch(IOException e) {
220            // Attributes can't throw checked exceptions, so convert this to an Error.
221            throw new Error(e);
222          }
223        }
224    
225      
226    
227        /**
228         * Add a package name to available package set.
229         */
230        public synchronized void addPackage(String packageName) {
231          int end = packageName.length();
232          while (end > 0 && packages.add(packageName.substring(0, end))) {
233            end = packageName.lastIndexOf('.', end-1);
234          }
235        }
236    
237      
238    
239        /**
240         * Add a path part to the library class path.
241         */
242        public synchronized void addClassPath(PathPart pathPart) {
243          classPath.add(pathPart);
244        }
245    
246      
247    
248        /**
249         * Add a path part to the user class path.
250         */
251        public synchronized void addSourcePath(PathPart pathPart) {
252          sourcePath.add(pathPart);
253        }
254    
255      
256    
257        /**
258         * Quick pass, slow fail. Cache existing package names in a concurrent set.
259         * @return <code>true</code> if there is a package with the given name on
260         * the classpath
261         */
262        public synchronized boolean isPackage(String packageName) {
263          initPaths();
264          if (packages.contains(packageName)) {
265            return true;
266          }
267          for (PathPart part: classPath) {
268            if (part.hasPackage(packageName)) {
269              addPackage(packageName);
270              return true;
271            }
272          }
273          for (PathPart part: sourcePath) {
274            if (part.hasPackage(packageName)) {
275              addPackage(packageName);
276              return true;
277            }
278          }
279          return false;
280        }
281    
282      
283    
284        /**
285         * @return a copy of the source path parts
286         */
287        public synchronized Collection<PathPart> getSourcePath() {
288          return new ArrayList<PathPart>(sourcePath);
289        }
290    
291      
292    
293        /**
294         * @return a copy of the class path parts
295         */
296        public synchronized Collection<PathPart> getClassPath() {
297          return new ArrayList<PathPart>(classPath);
298        }
299    
300    
301    }