001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    import java.io.File;
011    import java.util.*;
012    import beaver.*;
013    
014    aspect ClassPath {
015      interface BytecodeReader {
016        CompilationUnit read(InputStream is, String fullName, Program p) throws FileNotFoundException, IOException;
017      }
018      interface JavaParser {
019        CompilationUnit parse(InputStream is, String fileName) throws IOException, beaver.Parser.Exception;
020      }
021    
022      protected BytecodeReader Program.bytecodeReader;
023      public void Program.initBytecodeReader(BytecodeReader r) { bytecodeReader = r; }
024      protected JavaParser Program.javaParser;
025      public void Program.initJavaParser(JavaParser p) { javaParser = p; }
026    
027      syn String CompilationUnit.relativeName() = relativeName;
028      syn String CompilationUnit.pathName() = pathName;
029      syn boolean CompilationUnit.fromSource() = fromSource;
030    
031      inh CompilationUnit TypeDecl.compilationUnit();
032      eq CompilationUnit.getChild().compilationUnit() = this;
033    
034      /**
035       * Add a filename to the list of source files to process.
036       * @return The CompilationUnit representing the source file,
037       * or <code>null</code> if no such file exists
038       */
039      public CompilationUnit Program.addSourceFile(String name) {
040        return sourceFiles.addSourceFile(name);
041      }
042    
043      // iterate over all source files and demand-loaded compilation units
044      public Iterator Program.compilationUnitIterator() {
045        initPaths();
046        return new Iterator() {
047          int index = 0;
048          public boolean hasNext() {
049            return index < getNumCompilationUnit() || !sourceFiles.isEmpty();
050          }
051          public Object next() {
052            if(getNumCompilationUnit() == index) {
053              String typename = (String)sourceFiles.keySet().iterator().next();
054              CompilationUnit u = getCompilationUnit(typename);
055              if(u != null) {
056                addCompilationUnit(u);
057                getCompilationUnit(getNumCompilationUnit()-1);
058              }
059              else
060                throw new Error("File " + typename + " not found");
061            }
062            return getCompilationUnit(index++);
063          }
064          public void remove() {
065            throw new UnsupportedOperationException();
066          }
067        };
068      }
069      
070      // get the input stream for a compilation unit specified using
071      // a canonical name. This is used by the bytecode reader to load
072      // nested types
073      public InputStream Program.getInputStream(String name) {
074        initPaths();
075        try {
076          for(Iterator iter = classPath.iterator(); iter.hasNext(); ) {
077            PathPart part = (PathPart)iter.next();
078            if(part.selectCompilationUnit(name))
079              return part.is;
080          }
081        }
082        catch(IOException e) {
083        }
084        throw new Error("Could not find nested type " + name);
085      }
086    
087      refine public List Program.getCompilationUnitList() {
088        initPaths();
089        return refined();
090      }
091        
092      // load a compilation unit from disc using the following rules:
093      //   1) specified on the command line
094      //   2) class file not older than source file
095      //   3) source file
096      public CompilationUnit Program.getCompilationUnit(String name) {
097        initPaths();
098        try {
099          if(sourceFiles.selectCompilationUnit(name))
100            return sourceFiles.getCompilationUnit();
101          PathPart sourcePart = null;
102          PathPart classPart = null;
103          for(Iterator iter = sourcePath.iterator(); iter.hasNext() && sourcePart == null; ) {
104            PathPart part = (PathPart)iter.next();
105            if(part.selectCompilationUnit(name))
106              sourcePart = part;
107          }
108          for(Iterator iter = classPath.iterator(); iter.hasNext() && classPart == null; ) {
109            PathPart part = (PathPart)iter.next();
110            if(part.selectCompilationUnit(name))
111              classPart = part;
112          }
113          
114          if(sourcePart != null && (classPart == null || classPart.age < sourcePart.age)) {
115            CompilationUnit unit = sourcePart.getCompilationUnit();
116            int index = name.lastIndexOf('.');
117            if(index == -1)
118              return unit;
119            String pkgName = name.substring(0, index);
120            if(pkgName.equals(unit.getPackageDecl()))
121              return unit;
122          }
123          if(classPart != null) {
124            CompilationUnit unit = classPart.getCompilationUnit();
125            int index = name.lastIndexOf('.');
126            if(index == -1)
127              return unit;
128            String pkgName = name.substring(0, index);
129            if(pkgName.equals(unit.getPackageDecl()))
130              return unit;
131          }
132          return null;
133        }
134        catch(IOException e) {
135        }
136        return null;
137      }
138      
139      /**
140       * @return <code>true</code> if there is a package with the given name on
141       * the path
142       */
143      public boolean Program.isPackage(String name) {
144        if(sourceFiles.hasPackage(name))
145          return true;
146        for(Iterator iter = classPath.iterator(); iter.hasNext(); ) {
147          PathPart part = (PathPart)iter.next();
148          if(part.hasPackage(name))
149            return true;
150        }
151        for(Iterator iter = sourcePath.iterator(); iter.hasNext(); ) {
152          PathPart part = (PathPart)iter.next();
153          if(part.hasPackage(name))
154            return true;
155        }
156        return false;
157      }
158    
159      private String CompilationUnit.relativeName;
160      private String CompilationUnit.pathName;
161      private boolean CompilationUnit.fromSource;
162    
163      public void CompilationUnit.setRelativeName(String name) {
164        relativeName = name;
165      }
166      public void CompilationUnit.setPathName(String name) {
167        pathName = name;
168      }
169      public void CompilationUnit.setFromSource(boolean value) {
170        fromSource = value;
171      }
172    
173      private boolean Program.pathsInitialized = false;
174      private java.util.ArrayList Program.classPath;
175      private java.util.ArrayList Program.sourcePath;
176      private FileNamesPart Program.sourceFiles = new FileNamesPart(this);
177    
178      public void Program.pushClassPath(String name) {
179        PathPart part = PathPart.createSourcePath(name, this);
180        if(part != null) {
181          sourcePath.add(part);
182          System.out.println("Pushing source path " + name);
183        }
184        else
185          throw new Error("Could not push source path " + name);
186        part = PathPart.createClassPath(name, this);
187        if(part != null) {
188          classPath.add(part);
189          System.out.println("Pushing class path " + name);
190        }
191      }
192      public void Program.popClassPath() {
193        if(sourcePath.size() > 0)
194          sourcePath.remove(sourcePath.size()-1);
195        if(classPath.size() > 0)
196          classPath.remove(classPath.size()-1);
197      }
198    
199      public void Program.initPaths() {
200        if(!pathsInitialized) {
201          pathsInitialized = true;
202    
203          //System.err.println("Initializing class paths");
204          
205          ArrayList classPaths = new ArrayList();
206          ArrayList sourcePaths = new ArrayList();
207          
208          String[] bootclasspaths;
209          if(options().hasValueForOption("-bootclasspath"))
210            bootclasspaths = options().getValueForOption("-bootclasspath").split(File.pathSeparator);
211          else
212            bootclasspaths = System.getProperty("sun.boot.class.path").split(File.pathSeparator);
213          for(int i = 0; i < bootclasspaths.length; i++) {
214            classPaths.add(bootclasspaths[i]);
215            //System.err.println("Adding classpath " + bootclasspaths[i]);
216          }
217          
218          String[] extdirs;
219          if(options().hasValueForOption("-extdirs"))
220            extdirs = options().getValueForOption("-extdirs").split(File.pathSeparator);
221          else
222            extdirs = System.getProperty("java.ext.dirs").split(File.pathSeparator);
223          for(int i = 0; i < extdirs.length; i++) {
224            classPaths.add(extdirs[i]);
225            //System.err.println("Adding classpath " + extdirs[i]);
226          }
227    
228          String[] userClasses = null;
229          if(options().hasValueForOption("-classpath"))
230            userClasses = options().getValueForOption("-classpath").split(File.pathSeparator);
231          else if(options().hasValueForOption("-cp"))
232            userClasses = options().getValueForOption("-cp").split(File.pathSeparator);
233          else {
234            userClasses = ".".split(File.pathSeparator);
235          }
236          if(!options().hasValueForOption("-sourcepath")) {
237            for(int i = 0; i < userClasses.length; i++) {
238              classPaths.add(userClasses[i]);
239              sourcePaths.add(userClasses[i]);
240              //System.err.println("Adding classpath/sourcepath " + userClasses[i]);
241            }
242          }
243          else {
244            for(int i = 0; i < userClasses.length; i++) {
245              classPaths.add(userClasses[i]);
246              //System.err.println("Adding classpath " + userClasses[i]);
247            }
248            userClasses = options().getValueForOption("-sourcepath").split(File.pathSeparator);
249            for(int i = 0; i < userClasses.length; i++) {
250              sourcePaths.add(userClasses[i]);
251              //System.err.println("Adding sourcepath " + userClasses[i]);
252            }
253          }
254            
255          classPath = new ArrayList();
256          sourcePath = new ArrayList();
257          
258          for(Iterator iter = classPaths.iterator(); iter.hasNext(); ) {
259            String s = (String)iter.next();
260            PathPart part = PathPart.createClassPath(s, this);
261            if(part != null) {
262              classPath.add(part);
263              //System.out.println("Adding classpath " + s);
264            }
265            else if(options().verbose())
266              System.out.println("Warning: Could not use " + s + " as class path");
267          }
268          for(Iterator iter = sourcePaths.iterator(); iter.hasNext(); ) {
269            String s = (String)iter.next();
270            PathPart part = PathPart.createSourcePath(s, this);
271            if(part != null) {
272              sourcePath.add(part);
273              //System.out.println("Adding sourcepath " + s);
274            }
275            else if(options().verbose())
276              System.out.println("Warning: Could not use " + s + " as source path");
277          }
278        }
279      }
280    
281      /**
282       * Add a path part to the library class path.
283       */
284      public void Program.addClassPath(PathPart pathPart) {
285        classPath.add(pathPart);
286        pathPart.program = this;
287      }
288    
289      /**
290       * Add a path part to the user class path.
291       */
292      public void Program.addSourcePath(PathPart pathPart) {
293        sourcePath.add(pathPart);
294        pathPart.program = this;
295      }
296        
297      // remove user defined classes from this program but keep library classes
298      public void Program.simpleReset() {
299        lookupType_String_String_values = new HashMap();
300        hasPackage_String_values = new HashMap();
301        List list = new List();
302        for(int i = 0; i < getNumCompilationUnit(); i++) {
303          CompilationUnit unit = getCompilationUnit(i);
304          if(!unit.fromSource()) {
305            list.add(unit);
306          }
307        }
308        setCompilationUnitList(list);
309      }
310    }