/* * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered * by the modified BSD License. You should have received a copy of the * modified BSD license with this compiler. * * Copyright (c) 2005-2008, Torbjorn Ekman * All rights reserved. */ import java.io.File; import java.util.*; import beaver.*; aspect ClassPath { interface BytecodeReader { CompilationUnit read(InputStream is, String fullName, Program p) throws FileNotFoundException, IOException; } interface JavaParser { CompilationUnit parse(InputStream is, String fileName) throws IOException, beaver.Parser.Exception; } protected BytecodeReader Program.bytecodeReader; public void Program.initBytecodeReader(BytecodeReader r) { bytecodeReader = r; } protected JavaParser Program.javaParser; public void Program.initJavaParser(JavaParser p) { javaParser = p; } syn String CompilationUnit.relativeName() = relativeName; syn String CompilationUnit.pathName() = pathName; syn boolean CompilationUnit.fromSource() = fromSource; inh CompilationUnit TypeDecl.compilationUnit(); eq CompilationUnit.getChild().compilationUnit() = this; // add a filename to the list of source files to process public void Program.addSourceFile(String name) { sourceFiles.addSourceFile(name); } // iterate over all source files and demand-loaded compilation units public Iterator Program.compilationUnitIterator() { initPaths(); return new Iterator() { int index = 0; public boolean hasNext() { return index < getNumCompilationUnit() || !sourceFiles.isEmpty(); } public Object next() { if(getNumCompilationUnit() == index) { String typename = (String)sourceFiles.keySet().iterator().next(); CompilationUnit u = getCompilationUnit(typename); if(u != null) { addCompilationUnit(u); getCompilationUnit(getNumCompilationUnit()-1); } else throw new Error("File " + typename + " not found"); } return getCompilationUnit(index++); } public void remove() { throw new UnsupportedOperationException(); } }; } // get the input stream for a compilation unit specified using // a canonical name. This is used by the bytecode reader to load // nested types public InputStream Program.getInputStream(String name) { initPaths(); try { for(Iterator iter = classPath.iterator(); iter.hasNext(); ) { PathPart part = (PathPart)iter.next(); if(part.selectCompilationUnit(name)) return part.is; } } catch(IOException e) { } throw new Error("Could not find nested type " + name); } // load a compilation unit from disc using the following rules: // 1) specified on the command line // 2) class file not older than source file // 3) source file public CompilationUnit Program.getCompilationUnit(String name) { initPaths(); try { if(sourceFiles.selectCompilationUnit(name)) return sourceFiles.getCompilationUnit(); PathPart sourcePart = null; PathPart classPart = null; for(Iterator iter = sourcePath.iterator(); iter.hasNext() && sourcePart == null; ) { PathPart part = (PathPart)iter.next(); if(part.selectCompilationUnit(name)) sourcePart = part; } for(Iterator iter = classPath.iterator(); iter.hasNext() && classPart == null; ) { PathPart part = (PathPart)iter.next(); if(part.selectCompilationUnit(name)) classPart = part; } if(sourcePart != null && (classPart == null || classPart.age <= sourcePart.age)) { CompilationUnit unit = sourcePart.getCompilationUnit(); int index = name.lastIndexOf('.'); if(index == -1) return unit; String pkgName = name.substring(0, index); if(pkgName.equals(unit.getPackageDecl())) return unit; } if(classPart != null) { CompilationUnit unit = classPart.getCompilationUnit(); int index = name.lastIndexOf('.'); if(index == -1) return unit; String pkgName = name.substring(0, index); if(pkgName.equals(unit.getPackageDecl())) return unit; } return null; } catch(IOException e) { } return null; } // is there a package named name on the path public boolean Program.isPackage(String name) { if(sourceFiles.hasPackage(name)) return true; for(Iterator iter = classPath.iterator(); iter.hasNext(); ) { PathPart part = (PathPart)iter.next(); if(part.hasPackage(name)) return true; } for(Iterator iter = sourcePath.iterator(); iter.hasNext(); ) { PathPart part = (PathPart)iter.next(); if(part.hasPackage(name)) return true; } return false; } private String CompilationUnit.relativeName; private String CompilationUnit.pathName; private boolean CompilationUnit.fromSource; public void CompilationUnit.setRelativeName(String name) { relativeName = name; } public void CompilationUnit.setPathName(String name) { pathName = name; } public void CompilationUnit.setFromSource(boolean value) { fromSource = value; } private boolean Program.pathsInitialized = false; private java.util.ArrayList Program.classPath; private java.util.ArrayList Program.sourcePath; private FileNamesPart Program.sourceFiles = new FileNamesPart(this); public void Program.pushClassPath(String name) { PathPart part = PathPart.createSourcePath(name, this); if(part != null) { sourcePath.add(part); System.out.println("Pushing source path " + name); } else throw new Error("Could not push source path " + name); part = PathPart.createClassPath(name, this); if(part != null) { classPath.add(part); System.out.println("Pushing class path " + name); } } public void Program.popClassPath() { if(sourcePath.size() > 0) sourcePath.remove(sourcePath.size()-1); if(classPath.size() > 0) classPath.remove(classPath.size()-1); } public void Program.initPaths() { if(!pathsInitialized) { pathsInitialized = true; //System.err.println("Initializing class paths"); ArrayList classPaths = new ArrayList(); ArrayList sourcePaths = new ArrayList(); String[] bootclasspaths; if(Program.hasValueForOption("-bootclasspath")) bootclasspaths = Program.getValueForOption("-bootclasspath").split(File.pathSeparator); else bootclasspaths = System.getProperty("sun.boot.class.path").split(File.pathSeparator); for(int i = 0; i < bootclasspaths.length; i++) { classPaths.add(bootclasspaths[i]); //System.err.println("Adding classpath " + bootclasspaths[i]); } String[] extdirs; if(Program.hasValueForOption("-extdirs")) extdirs = Program.getValueForOption("-extdirs").split(File.pathSeparator); else extdirs = System.getProperty("java.ext.dirs").split(File.pathSeparator); for(int i = 0; i < extdirs.length; i++) { classPaths.add(extdirs[i]); //System.err.println("Adding classpath " + extdirs[i]); } String[] userClasses = null; if(Program.hasValueForOption("-classpath")) userClasses = Program.getValueForOption("-classpath").split(File.pathSeparator); else { String s = System.getProperty("java.class.path"); if(s != null && s.length() > 0) { s = s + File.pathSeparator + "."; // TODO; This should not be necessary userClasses = s.split(File.pathSeparator); } else userClasses = ".".split(File.pathSeparator); } if(!Program.hasValueForOption("-sourcepath")) { for(int i = 0; i < userClasses.length; i++) { classPaths.add(userClasses[i]); sourcePaths.add(userClasses[i]); //System.err.println("Adding classpath/sourcepath " + userClasses[i]); } } else { for(int i = 0; i < userClasses.length; i++) { classPaths.add(userClasses[i]); //System.err.println("Adding classpath " + userClasses[i]); } userClasses = Program.getValueForOption("-sourcepath").split(File.pathSeparator); for(int i = 0; i < userClasses.length; i++) { sourcePaths.add(userClasses[i]); //System.err.println("Adding sourcepath " + userClasses[i]); } } classPath = new ArrayList(); sourcePath = new ArrayList(); for(Iterator iter = classPaths.iterator(); iter.hasNext(); ) { String s = (String)iter.next(); PathPart part = PathPart.createClassPath(s, this); if(part != null) { classPath.add(part); //System.out.println("Adding classpath " + s); } else if(Program.verbose()) System.out.println("Warning: Could not use " + s + " as class path"); } for(Iterator iter = sourcePaths.iterator(); iter.hasNext(); ) { String s = (String)iter.next(); PathPart part = PathPart.createSourcePath(s, this); if(part != null) { sourcePath.add(part); //System.out.println("Adding sourcepath " + s); } else if(Program.verbose()) System.out.println("Warning: Could not use " + s + " as source path"); } } } class PathPart { public InputStream is; protected String pathName; protected String relativeName; protected String fullName; long age; Program program; protected PathPart() { } protected boolean isSource; protected String fileSuffix() { return isSource ? ".java" : ".class"; } public static PathPart createSourcePath(String fileName, Program program) { PathPart p = createPathPart(fileName); if(p != null) { p.isSource = true; p.program = program; } return p; } public static PathPart createClassPath(String fileName, Program program) { PathPart p = createPathPart(fileName); if(p != null) { p.isSource = false; p.program = program; } return p; } private static PathPart createPathPart(String s) { try { File f = new File(s); if(f.isDirectory()) return new FolderPart(f); else if(f.isFile()) return new ZipFilePart(new ZipFile(f)); } catch (IOException e) { // error in path } return null; } // is there a package with the specified name on this path part public boolean hasPackage(String name) { return false; } // select a compilation unit from a canonical name // returns true of the compilation unit exists on this path public boolean selectCompilationUnit(String canonicalName) throws IOException { return false; } // load the return currently selected compilation unit public CompilationUnit getCompilationUnit() { long startTime = System.currentTimeMillis(); if(!isSource) { try { if(Program.verbose()) System.out.print("Loading .class file: " + fullName + " "); CompilationUnit u = program.bytecodeReader.read(is, fullName, program); //CompilationUnit u = new bytecode.Parser(is, fullName).parse(null, null, program); u.setPathName(pathName); u.setRelativeName(relativeName); u.setFromSource(false); is.close(); is = null; if(Program.verbose()) System.out.println("from " + pathName + " in " + (System.currentTimeMillis() - startTime) + " ms"); return u; } catch (Exception e) { throw new Error("Error loading " + fullName, e); } } else { try { if(Program.verbose()) System.out.print("Loading .java file: " + fullName + " "); CompilationUnit u = program.javaParser.parse(is, fullName); is.close(); is = null; u.setPathName(pathName); u.setRelativeName(relativeName); u.setFromSource(true); if(Program.verbose()) System.out.println("in " + (System.currentTimeMillis() - startTime) + " ms"); return u; } catch (Exception e) { System.err.println("Unexpected error of kind " + e.getClass().getName()); throw new Error(fullName + ": " + e.getMessage(), e); } } } } // load files from a folder class FolderPart extends PathPart { private HashMap map = new HashMap(); private File folder; public FolderPart(File folder) { this.folder = folder; } public boolean hasPackage(String name) { return filesInPackage(name) != null; } public boolean hasCompilationUnit(String canonicalName) { int index = canonicalName.lastIndexOf('.'); String packageName = index == -1 ? "" : canonicalName.substring(0, index); String typeName = canonicalName.substring(index + 1, canonicalName.length()); Collection c = filesInPackage(packageName); boolean result = c != null && c.contains(typeName + fileSuffix()); return result; } private Collection filesInPackage(String packageName) { if(!map.containsKey(packageName)) { File f = new File(folder, packageName.replace('.', File.separatorChar)); Collection c = Collections.EMPTY_LIST; if(f.exists() && f.isDirectory()) { String[] files = f.list(); if(files.length > 0) { c = new HashSet(); for(int i = 0; i < files.length; i++) c.add(files[i]); } } else c = null; map.put(packageName, c); } return (Collection)map.get(packageName); } public boolean selectCompilationUnit(String canonicalName) throws IOException { if(hasCompilationUnit(canonicalName)) { String fileName = canonicalName.replace('.', File.separatorChar); File classFile = new File(folder, fileName + fileSuffix()); if(classFile.isFile()) { is = new FileInputStream(classFile); age = classFile.lastModified(); pathName = classFile.getAbsolutePath(); relativeName = fileName + fileSuffix(); fullName = canonicalName; return true; } } return false; } } // load files in a zip file class ZipFilePart extends PathPart { private HashSet set = new HashSet(); private ZipFile file; public boolean hasPackage(String name) { return set.contains(name); } public ZipFilePart(ZipFile file) { this.file = file; // process all entries in the zip file for (Enumeration e = file.entries() ; e.hasMoreElements() ;) { ZipEntry entry = (ZipEntry)e.nextElement(); String pathName = new File(entry.getName()).getParent(); if(pathName != null) pathName = pathName.replace(File.separatorChar, '.'); if(!set.contains(pathName)) { int pos = 0; while(pathName != null && -1 != (pos = pathName.indexOf('.', pos + 1))) { String n = pathName.substring(0, pos); if(!set.contains(n)) { set.add(n); } } set.add(pathName); } set.add(entry.getName()); } } public boolean selectCompilationUnit(String canonicalName) throws IOException { String name = canonicalName.replace('.', '/'); // ZipFiles do always use '/' as separator name = name + fileSuffix(); if(set.contains(name)) { ZipEntry zipEntry = file.getEntry(name); if(zipEntry != null && !zipEntry.isDirectory()) { is = file.getInputStream(zipEntry); age = zipEntry.getTime(); pathName = file.getName(); relativeName = name + fileSuffix(); fullName = canonicalName; return true; } } return false; } } // load files specified explicitly (on the command line) class FileNamesPart extends PathPart { private HashMap sourceFiles = new HashMap(); private HashSet packages = new HashSet(); public FileNamesPart(Program p) { isSource = true; program = p; } public boolean hasPackage(String name) { return packages.contains(name); } public boolean isEmpty() { return sourceFiles.isEmpty(); } public Collection keySet() { return sourceFiles.keySet(); } public boolean selectCompilationUnit(String canonicalName) throws IOException { if(sourceFiles.containsKey(canonicalName)) { String f = (String)sourceFiles.get(canonicalName); File classFile = new File(f); if(classFile.isFile()) { is = new FileInputStream(classFile); pathName = classFile.getAbsolutePath(); // TODO: check me""; relativeName = f; fullName = canonicalName; sourceFiles.remove(canonicalName); return true; } } return false; } public void addSourceFile(String name) { try { File classFile = new File(name); if(classFile.isFile()) { is = new FileInputStream(classFile); this.pathName = classFile.getAbsolutePath(); relativeName = name; fullName = name; // is this ok CompilationUnit u = getCompilationUnit(); if(u != null) { String packageName = u.getPackageDecl(); if(packageName != null && !packages.contains(packageName)) { packages.add(packageName); int pos = 0; while(packageName != null && -1 != (pos = packageName.indexOf('.', pos + 1))) { String n = packageName.substring(0, pos); if(!packages.contains(n)) packages.add(n); } } program.addCompilationUnit(u); } } } catch (IOException e) { } } } // remove user defined classes from this program but keep library classes public void Program.simpleReset() { lookupType_String_String_values = new HashMap(); hasPackage_String_values = new HashMap(); List list = new List(); for(int i = 0; i < getNumCompilationUnit(); i++) { CompilationUnit unit = getCompilationUnit(i); if(!unit.fromSource()) { list.add(unit); } } setCompilationUnitList(list); } }