/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