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 }