KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > util > IPathEnumerator


1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2  *
3  * This program and the accompanying materials are made available under
4  * the terms of the Common Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
6  *
7  * $Id: IPathEnumerator.java,v 1.1.1.1.2.1 2004/07/16 23:32:04 vlad_r Exp $
8  */

9 package com.vladium.util;
10
11 import java.io.BufferedInputStream JavaDoc;
12 import java.io.File JavaDoc;
13 import java.io.FileInputStream JavaDoc;
14 import java.io.FileNotFoundException JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Set JavaDoc;
19 import java.util.StringTokenizer JavaDoc;
20 import java.util.jar.Attributes JavaDoc;
21 import java.util.jar.JarFile JavaDoc;
22 import java.util.jar.JarInputStream JavaDoc;
23 import java.util.jar.Manifest JavaDoc;
24 import java.util.zip.ZipEntry JavaDoc;
25
26 import com.vladium.logging.Logger;
27 import com.vladium.util.asserts.$assert;
28
29 // ----------------------------------------------------------------------------
30
/**
31  * @author Vlad Roubtsov, (C) 2003
32  */

33 public
34 interface IPathEnumerator
35 {
36     // public: ................................................................
37

38     // TODO: archives inside archives? (.war ?)
39

40     public static interface IPathHandler
41     {
42         void handleDirStart (File JavaDoc pathDir, File JavaDoc dir); // not generated for path dirs themselves
43
void handleFile (File JavaDoc pathDir, File JavaDoc file);
44         void handleDirEnd (File JavaDoc pathDir, File JavaDoc dir);
45
46         /**
47          * Called just after the enumerator's zip input stream for this archive
48          * is opened and the manifest entry is read.
49          */

50         void handleArchiveStart (File JavaDoc parentDir, File JavaDoc archive, Manifest JavaDoc manifest);
51         
52         void handleArchiveEntry (JarInputStream JavaDoc in, ZipEntry JavaDoc entry);
53         
54         /**
55          * Called after the enumerator's zip input stream for this archive
56          * has been closed.
57          */

58         void handleArchiveEnd (File JavaDoc parentDir, File JavaDoc archive);
59         
60     } // end of nested interface
61

62     
63     void enumerate () throws IOException JavaDoc;
64
65     
66     public static abstract class Factory
67     {
68         public static IPathEnumerator create (final File JavaDoc [] path, final boolean canonical, final IPathHandler handler)
69         {
70             return new PathEnumerator (path, canonical, handler);
71         }
72         
73         private static final class PathEnumerator implements IPathEnumerator
74         {
75             public void enumerate () throws IOException JavaDoc
76             {
77                 final IPathHandler handler = m_handler;
78                 
79                 for (m_pathIndex = 0; m_pathIndex < m_path.size (); ++ m_pathIndex) // important not to cache m_path.size()
80
{
81                     final File JavaDoc f = (File JavaDoc) m_path.get (m_pathIndex);
82                     
83                     if (! f.exists ())
84                     {
85                         if (IGNORE_INVALID_ENTRIES)
86                             continue;
87                         else
88                             throw new IllegalArgumentException JavaDoc ("path entry does not exist: [" + f + "]");
89                     }
90                     
91                     
92                     if (f.isDirectory ())
93                     {
94                         if (m_verbose) m_log.verbose ("processing dir path entry [" + f.getAbsolutePath () + "] ...");
95                         
96                         m_currentPathDir = f;
97                         enumeratePathDir (null);
98                     }
99                     else
100                     {
101                         final String JavaDoc name = f.getName ();
102                         final String JavaDoc lcName = name.toLowerCase ();
103                         
104                         if (lcName.endsWith (".zip") || lcName.endsWith (".jar"))
105                         {
106                             if (m_verbose) m_log.verbose ("processing archive path entry [" + f.getAbsolutePath () + "] ...");
107                             
108                             final File JavaDoc parent = f.getParentFile (); // could be null
109
final File JavaDoc archive = new File JavaDoc (name);
110                             m_currentPathDir = parent;
111                         
112                             // move to enumeratePathArchive(): handler.handleArchiveStart (parent, archive);
113
enumeratePathArchive (name);
114                             handler.handleArchiveEnd (parent, archive); // note: it is important that this is called after the zip stream has been closed
115
}
116                         else if (! IGNORE_INVALID_ENTRIES)
117                         {
118                             throw new IllegalArgumentException JavaDoc ("path entry is not a directory or an archive: [" + f + "]");
119                         }
120                     }
121                 }
122             }
123             
124             PathEnumerator (final File JavaDoc [] path, final boolean canonical, final IPathHandler handler)
125             {
126                 m_path = new ArrayList JavaDoc (path.length);
127                 for (int p = 0; p < path.length; ++ p) m_path.add (path [p]);
128                 
129                 m_canonical = canonical;
130                 
131                 if (handler == null) throw new IllegalArgumentException JavaDoc ("null input: handler");
132                 m_handler = handler;
133                 
134                 m_processManifest = true; // TODO
135

136                 if (m_processManifest)
137                 {
138                     m_pathSet = new HashSet JavaDoc (path.length);
139                     for (int p = 0; p < path.length; ++ p)
140                     {
141                         m_pathSet.add (path [p].getPath ()); // set of [possibly canonical] paths
142
}
143                 }
144                 else
145                 {
146                     m_pathSet = null;
147                 }
148                 
149                 m_log = Logger.getLogger (); // each path enumerator caches its logger at creation time
150
m_verbose = m_log.atVERBOSE ();
151                 m_trace1 = m_log.atTRACE1 ();
152             }
153             
154             
155             private void enumeratePathDir (final String JavaDoc dir)
156                 throws IOException JavaDoc
157             {
158                 final boolean trace1 = m_trace1;
159                 
160                 final File JavaDoc currentPathDir = m_currentPathDir;
161                 final File JavaDoc fullDir = dir != null ? new File JavaDoc (currentPathDir, dir) : currentPathDir;
162                 
163                 final String JavaDoc [] children = fullDir.list ();
164                 final IPathHandler handler = m_handler;
165                 
166                 for (int c = 0, cLimit = children.length; c < cLimit; ++ c)
167                 {
168                     final String JavaDoc childName = children [c];
169                     
170                     final File JavaDoc child = dir != null ? new File JavaDoc (dir, childName) : new File JavaDoc (childName);
171                     final File JavaDoc fullChild = new File JavaDoc (fullDir, childName);
172                     
173                     if (fullChild.isDirectory ())
174                     {
175                         handler.handleDirStart (currentPathDir, child);
176                         if (trace1) m_log.trace1 ("enumeratePathDir", "recursing into [" + child.getName () + "] ...");
177                         enumeratePathDir (child.getPath ());
178                         handler.handleDirEnd (currentPathDir, child);
179                     }
180                     else
181                     {
182 // final String lcName = childName.toLowerCase ();
183
//
184
// if (lcName.endsWith (".zip") || lcName.endsWith (".jar"))
185
// {
186
// handler.handleArchiveStart (currentPathDir, child);
187
// enumeratePathArchive (child.getPath ());
188
// handler.handleArchiveEnd (currentPathDir, child);
189
// }
190
// else
191
{
192                             if (trace1) m_log.trace1 ("enumeratePathDir", "processing file [" + child.getName () + "] ...");
193                             handler.handleFile (currentPathDir, child);
194                         }
195                     }
196                 }
197             }
198             
199             private void enumeratePathArchive (final String JavaDoc archive)
200                 throws IOException JavaDoc
201             {
202                 final boolean trace1 = m_trace1;
203                 
204                 final File JavaDoc fullArchive = new File JavaDoc (m_currentPathDir, archive);
205                 
206                 JarInputStream JavaDoc in = null;
207                 try
208                 {
209                     // note: Sun's JarFile uses native code and has been known to
210
// crash the JVM in some builds; however, it uses random file
211
// access and can find "bad" manifests that are not the first
212
// entries in their archives (which JarInputStream can't do);
213
// [bugs: 4263225, 4696354, 4338238]
214
//
215
// there is really no good solution here but as a compromise
216
// I try to read the manifest again via a JarFile if the stream
217
// returns null for it:
218

219                     in = new JarInputStream JavaDoc (new BufferedInputStream JavaDoc (new FileInputStream JavaDoc (fullArchive), 32 * 1024));
220                     
221                     final IPathHandler handler = m_handler;
222                     
223                     Manifest JavaDoc manifest = in.getManifest (); // can be null
224
if (manifest == null) manifest = readManifestViaJarFile (fullArchive); // can be null
225

226                     handler.handleArchiveStart (m_currentPathDir, new File JavaDoc (archive), manifest);
227                     
228                     // note: this loop does not skip over the manifest-related
229
// entries [the handler needs to be smart about that]
230
for (ZipEntry JavaDoc entry; (entry = in.getNextEntry ()) != null; )
231                     {
232                         // TODO: handle nested archives
233

234                         if (trace1) m_log.trace1 ("enumeratePathArchive", "processing archive entry [" + entry.getName () + "] ...");
235                         handler.handleArchiveEntry (in, entry);
236                         in.closeEntry ();
237                     }
238                     
239                     
240                     // TODO: this needs major testing
241
if (m_processManifest)
242                     {
243                         // note: JarInputStream only reads the manifest if it the
244
// first jar entry
245
if (manifest == null) manifest = in.getManifest ();
246                         if (manifest != null)
247                         {
248                             final Attributes JavaDoc attributes = manifest.getMainAttributes ();
249                             if (attributes != null)
250                             {
251                                 // note: Sun's documentation says that multiple Class-Path:
252
// entries are merged sequentially (http://java.sun.com/products/jdk/1.2/docs/guide/extensions/spec.html)
253
// however, their own code does not implement this
254
final String JavaDoc jarClassPath = attributes.getValue (Attributes.Name.CLASS_PATH);
255                                 if (jarClassPath != null)
256                                 {
257                                     final StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc (jarClassPath);
258                                     for (int p = 1; tokenizer.hasMoreTokens (); )
259                                     {
260                                         final String JavaDoc relPath = tokenizer.nextToken ();
261                                         
262                                         final File JavaDoc archiveParent = fullArchive.getParentFile ();
263                                         final File JavaDoc path = archiveParent != null ? new File JavaDoc (archiveParent, relPath) : new File JavaDoc (relPath);
264                                         
265                                         final String JavaDoc fullPath = m_canonical ? Files.canonicalizePathname (path.getPath ()) : path.getPath ();
266                                         
267                                         if (m_pathSet.add (fullPath))
268                                         {
269                                             if (m_verbose) m_log.verbose (" added manifest Class-Path entry [" + path + "]");
270                                             m_path.add (m_pathIndex + (p ++), path); // insert after the current m_path entry
271
}
272                                     }
273                                 }
274                             }
275                         }
276                     }
277                 }
278                 catch (FileNotFoundException JavaDoc fnfe) // ignore: this should not happen
279
{
280                     if ($assert.ENABLED) throw fnfe;
281                 }
282                 finally
283                 {
284                     if (in != null) try { in.close (); } catch (Exception JavaDoc ignore) {}
285                 }
286             }
287             
288             
289             // see comments at the start of enumeratePathArchive()
290

291             private static Manifest JavaDoc readManifestViaJarFile (final File JavaDoc archive)
292             {
293                 Manifest JavaDoc result = null;
294                 
295                 JarFile JavaDoc jarfile = null;
296                 try
297                 {
298                     jarfile = new JarFile JavaDoc (archive, false); // 3-arg constructor is not in J2SE 1.2
299
result = jarfile.getManifest ();
300                 }
301                 catch (IOException JavaDoc ignore)
302                 {
303                 }
304                 finally
305                 {
306                     if (jarfile != null) try { jarfile.close (); } catch (IOException JavaDoc ignore) {}
307                 }
308                 
309                 return result;
310             }
311             
312
313             private final ArrayList JavaDoc /* File */ m_path;
314             private final boolean m_canonical;
315             private final Set JavaDoc /* String */ m_pathSet;
316             private final IPathHandler m_handler;
317             private final boolean m_processManifest;
318             
319             private final Logger m_log;
320             private boolean m_verbose, m_trace1;
321             
322             private int m_pathIndex;
323             private File JavaDoc m_currentPathDir;
324             
325             // if 'true', non-existent or non-archive or non-directory path entries
326
// will be silently ignored:
327
private static final boolean IGNORE_INVALID_ENTRIES = true; // this is consistent with the normal JVM behavior
328

329         } // end of nested class
330

331     } // end of nested class
332

333 } // end of interface
334
// ----------------------------------------------------------------------------
Popular Tags