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 org.apache.bcel.classfile.*;
013 import java.util.*;
014 import java.util.zip.*;
015 import java.io.*;
016 import EVolve.util.SceneSetting;
017 import EVolve.Scene;
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 ArrayList additional = SceneSetting.v().getAdditionalClassPath();
075 for (int i=0; i<additional.size(); i++) {
076 classPath.add(additional.get(i));
077 }
078 }
079
080 private void prepareSourcePath() {
081 String path = System.getProperty("java.library.path");
082 StringTokenizer token = new StringTokenizer(path,System.getProperty("path.separator"));
083
084 sourcePath.clear();
085 while (token.hasMoreTokens()) {
086 sourcePath.add(token.nextToken());
087 }
088 ArrayList additional = SceneSetting.v().getSourcePath();
089 for (int i=0; i<additional.size(); i++) {
090 sourcePath.add(additional.get(i));
091 }
092 }
093
094 private JavaClass loadClass(String className) {
095 JavaClass returnVal = null;
096
097 for (int i=0; i<classPath.size(); i++) {
098 String path = (String)classPath.get(i);
099 File f = new File(path);
100
101 if (!f.exists()) continue;
102
103 if (isArchive(f)) {
104 returnVal = processArchive(className,f);
105 } else if (isClassFile(f)) {
106 returnVal = processClassFile(className, f);
107 } else if (f.isDirectory()) {
108 returnVal = processDirectory(className, f);
109 }
110
111 if (returnVal != null) break;
112 }
113
114 return returnVal;
115 }
116
117 private boolean isArchive(File f) {
118 if (f.isFile() && f.canRead()) {
119 String path;
120 try {
121 path = f.getCanonicalPath();
122 } catch(IOException e) {
123 return false;
124 }
125
126 if(path.endsWith("zip") || path.endsWith("jar")) {
127 return true;
128 }
129 }
130 return false;
131 }
132
133 private boolean isClassFile(File file) {
134 try {
135 if (!file.isFile()) {
136 return false;
137 }
138 DataInputStream stream = new DataInputStream(new FileInputStream(file));
139
140 String path = file.getCanonicalPath();
141
142 if (path.endsWith(".class") && checkMagic(stream)) {
143 return true;
144 }
145
146 return false;
147 } catch (IOException e) {
148 return false;
149 }
150 }
151
152 private boolean checkMagic(DataInputStream classFileStream) {
153 try {
154 return ((classFileStream.readInt() & 0xFFFFFFFFL) == 0xCAFEBABEL);
155 } catch (IOException e) {
156 return false;
157 }
158 }
159
160 private JavaClass processArchive(String className, File archive) {
161 ZipFile zip;
162
163 try {
164 zip = new ZipFile(archive);
165 } catch (ZipException e) {
166 return null;
167 } catch (IOException e) {
168 return null;
169 }
170
171 Enumeration enum = zip.entries();
172 while (enum.hasMoreElements()) {
173 ZipEntry entry = (ZipEntry) enum.nextElement();
174 String entryName = entry.getName(), path;
175
176 /* make sure that the class name is valid */
177 if (entryName != null && entryName.endsWith(".class")) {
178 String tempClassName = entryName.substring(0, entryName.length() - 6).replace('/', '.');
179 try {
180 path = archive.getCanonicalPath();
181 if (tempClassName.equals(className)) {
182 return new ClassParser(path, entryName).parse();
183 }
184 } catch (IOException e) {
185 return null;
186 }
187 }
188 }
189
190 return null;
191 }
192
193 private JavaClass processClassFile(String className, File classFile) {
194 JavaClass javaClass = getClass(classFile);
195
196 if (javaClass == null) return null;
197 /* This method is only invoked when the file exists and is a regular file,
198 so these conditions are not checked again */
199 String tempClassName = javaClass.getClassName();
200 if (tempClassName.indexOf('.') >= 0) {
201 /* This class file is part of a package, which does not match
202 the location of the candidate file */
203 return null;
204 }
205 if (tempClassName.equals(className)) {
206 return javaClass;
207 }
208 return null;
209 }
210
211 private JavaClass processDirectory(String className, File dir) {
212 JavaClass javaClass;
213
214 /* If the class we are looking for is located in this directory,
215 then theoreticalPath must represent its location */
216 String theoreticalPath = dir.getPath() + File.separator + className.replace('.', File.separatorChar) + ".class";
217 File theoreticalFile = new File(theoreticalPath);
218
219
220 if (theoreticalFile.exists() && theoreticalFile.isFile()) {
221 javaClass = getClass(theoreticalFile);
222
223 if (javaClass == null) return null;
224 /* This could be a match */
225 String tempClassName = javaClass.getClassName();
226 if (tempClassName != null && tempClassName.equals(className)) {
227 /* This is a match */
228 return javaClass;
229 }
230 }
231
232 return null;
233 }
234
235 private JavaClass getClass(File classFile) {
236 try {
237 DataInputStream stream = new DataInputStream(new FileInputStream(classFile));
238
239 ClassParser classParser = new ClassParser(stream, classFile.getName());
240 return classParser.parse();
241 } catch (FileNotFoundException e) {
242 return null;
243 } catch (IOException e) {
244 return null;
245 }
246 }
247 }