/Users/lyon/j4p/src/classUtils/pack/util/SimpleClassPackageExplorer.java
|
1 package classUtils.pack.util;
2
3 import classUtils.pack.util.sis.StateInfoSupport;
4 import classUtils.pack.util.util.CPoolReader;
5
6 import java.io.*;
7 import java.util.*;
8 import java.util.jar.JarEntry;
9 import java.util.jar.JarFile;
10 import java.util.regex.Pattern;
11
12 /**
13 * A package explorer implementation.
14 * <p>
15 * WARNING: Sealed Jars aren't supported yet.
16 *
17 * @version 1.0
18 * @author Cristiano Sadun
19 */
20 public class SimpleClassPackageExplorer implements ClassPackageExplorer {
21
22 private String classPath;
23 private Pattern [] dirPatterns;
24 private StateInfoSupport sis;
25 private CPoolReader cpr;
26 private boolean errorOccurred;
27
28 /*
29 * A map (package name -> List(File files containing classes in that package)).
30 */
31 private Map filesPerPackage;
32
33 /*
34 * A map (package name -> Integer ).
35 */
36 private Map packageStatus;
37
38 /*
39 * A map (package name -> List(class names).
40 */
41 private Map classesPerPackage;
42
43 /*
44 * A map (class name -> File containing that class)
45 */
46 private Map filesPerClass; // NOT IMPLEMENTED YET
47
48 private static FileFilter classFileFilter = new ExtensionFileFilter("class");
49 private static FileFilter dirFileFilter = new DirectoryFileFilter();
50
51 /**
52 * Create a SimpleClassPackageExplorer on the system class path.
53 */
54 public SimpleClassPackageExplorer() {
55 this(System.getProperty("java.class.path"));
56 }
57
58 /**
59 * Create a SimpleClassPackageExplorer on the given class path.
60 *
61 * @param the classpath string to iterate on
62 */
63 public SimpleClassPackageExplorer(String classPath) {
64 this(classPath, new String[] { ".*" });
65 this.cpr=new CPoolReader(classPath);
66 }
67
68 /**
69 * Create a SimpleClassPackageExplorer on the given class path.
70 *
71 * @param the classpath string to iterate on
72 * @param classDirs an array of regular expression for the names of subdirectories to search for;
73 * these patterns are not considered for JARs.
74 */
75 public SimpleClassPackageExplorer(String classPath, String[] classDirs) {
76 this.classPath=classPath;
77 dirPatterns=new Pattern[classDirs.length];
78 for(int i=0;i<classDirs.length;i++) {
79 dirPatterns[i]=Pattern.compile(classDirs[i]);
80 }
81 }
82
83 /**
84 * @see classUtils.pack.util.ClassPackageExplorer#listPackage(String)
85 */
86 public String[] listPackage(String packageName) {
87 return listPackage(packageName, IN_DIRECTORY+IN_JAR+IN_JAR_SEALED);
88 }
89
90 /**
91 * @see classUtils.pack.util.ClassPackageExplorer#listPackageNames()
92 */
93 public String[] listPackageNames() {
94 return listPackageNames(false);
95 }
96
97 /**
98 * Method buildInfo.
99 */
100 private void buildInfo() {
101 sis = new StateInfoSupport();
102 filesPerPackage = new HashMap();
103 packageStatus = new HashMap();
104 classesPerPackage = new HashMap();
105 errorOccurred=false;
106
107 for(ClassPathIterator i=new ClassPathIterator(classPath);i.hasNext();) {
108 File entry=i.nextEntryFile();
109 if (i.isJar())
110 scanJarFile(entry);
111 else
112 scanDirectory(entry);
113 }
114 }
115
116 /**
117 * Method scanDirectory.
118 * @param entry
119 */
120 private void scanDirectory(File entry) {
121 // Is the entry matching one of the patterns?
122 boolean matchedOne=false;
123 for(int i=0;i<dirPatterns.length;i++) {
124 if (dirPatterns[i].matcher(entry.getName()).matches()) {
125 matchedOne=true;
126 break;
127 }
128 }
129 if (!matchedOne) return; // Skip the entry
130
131 // Fetch all the .class files
132 File [] classFiles = entry.listFiles(classFileFilter);
133 for(int i=0;i<classFiles.length;i++) {
134 try {
135 //System.out.println("processing "+classFiles[i]);
136 processFile(entry, false, new BufferedInputStream(new FileInputStream(classFiles[i])));
137 } catch (IOException e) {
138 sis.addEntry("Could not open/process class file "+classFiles[i].getAbsolutePath());
139 errorOccurred=true;
140 }
141 }
142
143 // Fetch all the subdirectories
144 File [] subDirs = entry.listFiles(dirFileFilter);
145
146 // Recurse
147 for(int i=0;i<subDirs.length;i++) {
148 scanDirectory(subDirs[i]);
149 }
150 }
151
152 /**
153 * Method scanJarFile.
154 * @param entry
155 */
156 private void scanJarFile(File entry) {
157 try {
158 JarFile jf = new JarFile(entry);
159 Enumeration e = jf.entries();
160 while(e.hasMoreElements()) {
161 JarEntry jarEntry = (JarEntry)e.nextElement();
162 if (jarEntry.isDirectory()) continue;
163 String jarEntryName=jarEntry.getName();
164 if (jarEntryName.endsWith(".class")) {
165 //System.out.println("processing "+jarEntry.getName());
166 processFile(entry, true, new BufferedInputStream(jf.getInputStream(jarEntry)));
167 }
168 }
169 } catch (IOException e) {
170 sis.addEntry("Scanning Jar file", "Could not open/process jar file \""+entry+"\"", e);
171 errorOccurred=true;
172 }
173 }
174
175 /**
176 * Method processFiles.
177 * @param entry
178 * @param classFiles
179 */
180 private void processFile(File entry, boolean isJar, InputStream classDataInputStream) throws IOException {
181
182 CPoolReader.classfile c = cpr.readClassData(classDataInputStream);
183
184 String className = c.getCPClassName(true);
185 String pkgName = getPkgName(className);
186
187 List files = (List)filesPerPackage.get(pkgName);
188 if (files==null) {
189 files=new ArrayList();
190 filesPerPackage.put(pkgName, files);
191 }
192 files.add(entry);
193
194 Integer status = (Integer)packageStatus.get(pkgName);
195 if (status==null) {
196 status=new Integer(0);
197 packageStatus.put(pkgName, status);
198 }
199
200 Integer newStatus = new Integer(status.intValue() | (isJar ? IN_JAR : IN_DIRECTORY));
201 packageStatus.put(pkgName, status);
202
203 List classes = (List)classesPerPackage.get(pkgName);
204 if (classes==null) {
205 classes=new ArrayList();
206 classesPerPackage.put(pkgName, classes);
207 }
208 classes.add(className);
209 }
210
211 /**
212 * Method getPkgName.
213 * @param className
214 * @return String
215 */
216 private String getPkgName(String className) {
217 int i=className.lastIndexOf('.');
218 if (i==-1) return className;
219 return className.substring(0,i);
220 }
221
222
223 /**
224 * Returns the classPath.
225 * @return String
226 */
227 public String getClassPath() {
228 return classPath;
229 }
230
231 /**
232 * @see classUtils.pack.util.ClassPackageExplorer#getPackageFiles(String)
233 */
234 public synchronized File[] getPackageFiles(String packageName) {
235 List l = (List)filesPerPackage.get(packageName);
236 if (l==null) return new File[0];
237 File [] files = new File[l.size()];
238 l.toArray(files);
239 return files;
240 }
241
242 /**
243 * @see classUtils.pack.util.ClassPackageExplorer#getStatus(String)
244 */
245 public synchronized int getStatus(String packageName) {
246 Integer i = (Integer)packageStatus.get(packageName);
247 if (i==null) return -1;
248 return i.intValue();
249 }
250
251 /**
252 * @see classUtils.pack.util.ClassPackageExplorer#listPackage(String, int)
253 */
254 public synchronized String[] listPackage(String packageName, int status) {
255 if (status != IN_DIRECTORY + IN_JAR + IN_JAR_SEALED)
256 throw new UnsupportedOperationException("Disaggregation by status not supported yet");
257 if (classesPerPackage==null) buildInfo();
258
259 List l;
260 if ((l=(List)classesPerPackage.get(packageName))==null) return new String[0];
261 String [] classes = new String[l.size()];
262 l.toArray(classes);
263 return classes;
264 }
265
266 /**
267 * @see classUtils.pack.util.ClassPackageExplorer#listPackageNames(boolean)
268 */
269 public synchronized String[] listPackageNames(boolean rescan) {
270 if (rescan || classesPerPackage==null) buildInfo();
271 String [] names = new String[classesPerPackage.keySet().size()];
272 classesPerPackage.keySet().toArray(names);
273 return names;
274 }
275
276 /**
277 * Sets the classPath.
278 * @param classPath The classPath to set
279 */
280 public synchronized void setClassPath(String classPath) {
281 this.classPath = classPath;
282 buildInfo();
283 }
284
285 /**
286 * @see classUtils.pack.util.ClassPackageExplorer#getErrorLog()
287 */
288 public synchronized String getErrorLog() {
289 if (sis==null) buildInfo();
290 return sis.getStateDescription(null);
291 }
292
293 /**
294 * A test method
295 */
296 public static void main(String args[]) throws Exception {
297 //ClassPackageExplorer explorer = new SimpleClassPackageExplorer("c:\\projects\\objectmapper\\classes");
298 SimpleClassPackageExplorer explorer = new SimpleClassPackageExplorer("c:\\projects\\jutil\\org.sadun.util.jar");
299 //ClassPackageExplorer explorer = new SimpleClassPackageExplorer();
300 String [] pkgs = explorer.listPackageNames();
301 System.out.println(ObjectLister.getInstance().list(pkgs));
302 if (pkgs.length > 0)
303 System.out.println(ObjectLister.getInstance().list(explorer.listPackage(pkgs[0])));
304 System.out.println(explorer.sis.getStateDescription(null));
305 }
306
307 /**
308 * @see classUtils.pack.util.ClassPackageExplorer#hasErrorOccurred()
309 */
310 public synchronized boolean hasErrorOccurred() {
311 if (sis==null) buildInfo();
312 return errorOccurred;
313 }
314
315 }
316