001 /** 002 * Created by IntelliJ IDEA. 003 * User: Wei Wang 004 * Date: Jan 15, 2003 005 * Time: 1:19:37 PM 006 * Some codes are attributed to ClassPathExplorer.java in Bruno Dufour's AdaptC toolkit. 007 * please refer to http://www.sable.mcgill.ca/~bdufou1/ for more details 008 */ 009 010 package EVolve.util.sourcebrowser; 011 012 import java.io.*; 013 import java.util.*; 014 import java.util.zip.*; 015 import EVolve.Scene; 016 import org.apache.bcel.classfile.*; 017 import EVolve.util.settings.SceneSetting; 018 019 public class ClassExplorer { 020 private HashMap mapClassToSourcename; 021 private ArrayList classPath,sourcePath; 022 private static ClassExplorer instance = null; 023 024 public ClassExplorer() { 025 mapClassToSourcename = new HashMap(); 026 classPath = new ArrayList(); 027 sourcePath = new ArrayList(); 028 } 029 030 public static ClassExplorer v() { 031 if (instance == null) instance = new ClassExplorer(); 032 return instance; 033 } 034 035 public String getSourceFileFromClass(String className) { 036 prepareClassPath(); 037 prepareSourcePath(); 038 039 if (!mapClassToSourcename.containsKey(className)) { 040 041 JavaClass javaClass = loadClass(className); 042 043 if (javaClass != null) { 044 String sourceName = javaClass.getSourceFileName(); 045 boolean fileFound = false; 046 047 for (int i=0; i<sourcePath.size(); i++) { 048 String path = (String)sourcePath.get(i)+File.separatorChar+sourceName; 049 File f = new File(path); 050 if (f.exists()) { 051 mapClassToSourcename.put(className, path); 052 fileFound = true; 053 break; 054 } 055 } 056 057 if (!fileFound) { 058 Scene.showErrorMessage("Can not find source file \""+sourceName+"\". \n"); 059 } 060 } 061 } 062 063 return (String)mapClassToSourcename.get(className); 064 } 065 066 private void prepareClassPath() { 067 String path = System.getProperty("java.class.path"); 068 StringTokenizer token = new StringTokenizer(path,System.getProperty("path.separator")); 069 070 classPath.clear(); 071 while (token.hasMoreTokens()) { 072 classPath.add(token.nextToken()); 073 } 074 SceneSetting.v().readDataFromFile(); 075 ArrayList additional = SceneSetting.v().getAdditionalClassPath(); 076 for (int i=0; i<additional.size(); i++) { 077 classPath.add(additional.get(i)); 078 } 079 } 080 081 private void prepareSourcePath() { 082 String path = System.getProperty("java.library.path"); 083 StringTokenizer token = new StringTokenizer(path,System.getProperty("path.separator")); 084 085 sourcePath.clear(); 086 while (token.hasMoreTokens()) { 087 sourcePath.add(token.nextToken()); 088 } 089 ArrayList additional = SceneSetting.v().getSourcePath(); 090 for (int i=0; i<additional.size(); i++) { 091 sourcePath.add(additional.get(i)); 092 } 093 } 094 095 private JavaClass loadClass(String className) { 096 JavaClass returnVal = null; 097 098 for (int i=0; i<classPath.size(); i++) { 099 String path = (String)classPath.get(i); 100 File f = new File(path); 101 102 if (!f.exists()) continue; 103 104 if (isArchive(f)) { 105 returnVal = processArchive(className,f); 106 } else if (isClassFile(f)) { 107 returnVal = processClassFile(className, f); 108 } else if (f.isDirectory()) { 109 returnVal = processDirectory(className, f); 110 } 111 112 if (returnVal != null) break; 113 } 114 115 return returnVal; 116 } 117 118 private boolean isArchive(File f) { 119 if (f.isFile() && f.canRead()) { 120 String path; 121 try { 122 path = f.getCanonicalPath(); 123 } catch(IOException e) { 124 return false; 125 } 126 127 if(path.endsWith("zip") || path.endsWith("jar")) { 128 return true; 129 } 130 } 131 return false; 132 } 133 134 private boolean isClassFile(File file) { 135 try { 136 if (!file.isFile()) { 137 return false; 138 } 139 DataInputStream stream = new DataInputStream(new FileInputStream(file)); 140 141 String path = file.getCanonicalPath(); 142 143 if (path.endsWith(".class") && checkMagic(stream)) { 144 return true; 145 } 146 147 return false; 148 } catch (IOException e) { 149 return false; 150 } 151 } 152 153 private boolean checkMagic(DataInputStream classFileStream) { 154 try { 155 return ((classFileStream.readInt() & 0xFFFFFFFFL) == 0xCAFEBABEL); 156 } catch (IOException e) { 157 return false; 158 } 159 } 160 161 private JavaClass processArchive(String className, File archive) { 162 ZipFile zip; 163 164 try { 165 zip = new ZipFile(archive); 166 } catch (ZipException e) { 167 return null; 168 } catch (IOException e) { 169 return null; 170 } 171 172 Enumeration enum = zip.entries(); 173 while (enum.hasMoreElements()) { 174 ZipEntry entry = (ZipEntry) enum.nextElement(); 175 String entryName = entry.getName(), path; 176 177 /* make sure that the class name is valid */ 178 if (entryName != null && entryName.endsWith(".class")) { 179 String tempClassName = entryName.substring(0, entryName.length() - 6).replace('/', '.'); 180 try { 181 path = archive.getCanonicalPath(); 182 if (tempClassName.equals(className)) { 183 return new ClassParser(path, entryName).parse(); 184 } 185 } catch (IOException e) { 186 return null; 187 } 188 } 189 } 190 191 return null; 192 } 193 194 private JavaClass processClassFile(String className, File classFile) { 195 JavaClass javaClass = getClass(classFile); 196 197 if (javaClass == null) return null; 198 /* This method is only invoked when the file exists and is a regular file, 199 so these conditions are not checked again */ 200 String tempClassName = javaClass.getClassName(); 201 if (tempClassName.indexOf('.') >= 0) { 202 /* This class file is part of a package, which does not match 203 the location of the candidate file */ 204 return null; 205 } 206 if (tempClassName.equals(className)) { 207 return javaClass; 208 } 209 return null; 210 } 211 212 private JavaClass processDirectory(String className, File dir) { 213 JavaClass javaClass; 214 215 /* If the class we are looking for is located in this directory, 216 then theoreticalPath must represent its location */ 217 String theoreticalPath = dir.getPath() + File.separator + className.replace('.', File.separatorChar) + ".class"; 218 File theoreticalFile = new File(theoreticalPath); 219 220 221 if (theoreticalFile.exists() && theoreticalFile.isFile()) { 222 javaClass = getClass(theoreticalFile); 223 224 if (javaClass == null) return null; 225 /* This could be a match */ 226 String tempClassName = javaClass.getClassName(); 227 if (tempClassName != null && tempClassName.equals(className)) { 228 /* This is a match */ 229 return javaClass; 230 } 231 } 232 233 return null; 234 } 235 236 private JavaClass getClass(File classFile) { 237 try { 238 DataInputStream stream = new DataInputStream(new FileInputStream(classFile)); 239 240 ClassParser classParser = new ClassParser(stream, classFile.getName()); 241 return classParser.parse(); 242 } catch (FileNotFoundException e) { 243 return null; 244 } catch (IOException e) { 245 return null; 246 } 247 } 248 }