KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jode > bytecode > SearchPath


1 /* SearchPath Copyright (C) 1998-2002 Jochen Hoenicke.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; see the file COPYING.LESSER. If not, write to
15  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  * $Id: SearchPath.java,v 4.18.2.3 2002/05/28 17:34:01 hoenicke Exp $
18  */

19
20 package jode.bytecode;
21 import java.io.ByteArrayInputStream JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileNotFoundException JavaDoc;
25 import java.io.FilterInputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.net.MalformedURLException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.URLConnection JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.Hashtable JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34 import java.util.Vector JavaDoc;
35 import java.util.zip.ZipEntry JavaDoc;
36 import java.util.zip.ZipFile JavaDoc;
37 import java.util.zip.ZipInputStream JavaDoc;
38 import jode.GlobalOptions;
39
40 /**
41  * This class represents a path of multiple directories and/or zip files,
42  * where we can search for file names.
43  *
44  * @author Jochen Hoenicke
45  */

46 public class SearchPath {
47
48     /**
49      * We need a different pathSeparatorChar, since ':' (used for most
50      * UNIX System) is used a protocol separator in URLs.
51      *
52      * We currently allow both pathSeparatorChar and
53      * altPathSeparatorChar and decide if it is a protocol separator
54      * by context.
55      */

56     public static final char altPathSeparatorChar = ',';
57     URL JavaDoc[] bases;
58     byte[][] urlzips;
59     File JavaDoc[] dirs;
60     ZipFile JavaDoc[] zips;
61     String JavaDoc[] zipDirs;
62     Hashtable JavaDoc[] zipEntries;
63
64     private static void addEntry(Hashtable JavaDoc entries, String JavaDoc name) {
65     String JavaDoc dir = "";
66     int pathsep = name.lastIndexOf("/");
67     if (pathsep != -1) {
68         dir = name.substring(0, pathsep);
69         name = name.substring(pathsep+1);
70     }
71
72     Vector JavaDoc dirContent = (Vector JavaDoc) entries.get(dir);
73     if (dirContent == null) {
74         dirContent = new Vector JavaDoc();
75         entries.put(dir, dirContent);
76         if (dir != "")
77         addEntry(entries, dir);
78     }
79     dirContent.addElement(name);
80     }
81
82     private void fillZipEntries(int nr) {
83     Enumeration JavaDoc zipEnum = zips[nr].entries();
84     zipEntries[nr] = new Hashtable JavaDoc();
85     while (zipEnum.hasMoreElements()) {
86         ZipEntry JavaDoc ze = (ZipEntry JavaDoc) zipEnum.nextElement();
87         String JavaDoc name = ze.getName();
88 // if (name.charAt(0) == '/')
89
// name = name.substring(1);
90
if (zipDirs[nr] != null) {
91         if (!name.startsWith(zipDirs[nr]))
92             continue;
93         name = name.substring(zipDirs[nr].length());
94         }
95         if (!ze.isDirectory() && name.endsWith(".class"))
96         addEntry(zipEntries[nr], name);
97     }
98     }
99
100     private void readURLZip(int nr, URLConnection JavaDoc conn) {
101     int length = conn.getContentLength();
102     if (length <= 0)
103         // Give a approximation if length is unknown
104
length = 10240;
105     else
106         // Increase the length by one, so we hopefully don't need
107
// to grow the array later (we need a little overshot to
108
// know when the end is reached).
109
length++;
110
111     urlzips[nr] = new byte[length];
112     try {
113         InputStream JavaDoc is = conn.getInputStream();
114         int pos = 0;
115         for (;;) {
116         // This is ugly, is.available() may return zero even
117
// if there are more bytes.
118
int avail = Math.max(is.available(), 1);
119         if (pos + is.available() > urlzips[nr].length) {
120             // grow the byte array.
121
byte[] newarr = new byte
122             [Math.max(2*urlzips[nr].length, pos + is.available())];
123             System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
124             urlzips[nr] = newarr;
125         }
126         int count = is.read(urlzips[nr], pos, urlzips[nr].length-pos);
127         if (count == -1)
128             break;
129         pos += count;
130         }
131         if (pos < urlzips[nr].length) {
132         // shrink the byte array again.
133
byte[] newarr = new byte[pos];
134         System.arraycopy(urlzips[nr], 0, newarr, 0, pos);
135         urlzips[nr] = newarr;
136         }
137     } catch (IOException JavaDoc ex) {
138         GlobalOptions.err.println("IOException while reading "
139                    +"remote zip file "+bases[nr]);
140         // disable entry
141
bases[nr] = null;
142         urlzips[nr] = null;
143         return;
144     }
145     try {
146         // fill entries into hash table
147
ZipInputStream JavaDoc zis = new ZipInputStream JavaDoc
148         (new ByteArrayInputStream JavaDoc(urlzips[nr]));
149         zipEntries[nr] = new Hashtable JavaDoc();
150         ZipEntry JavaDoc ze;
151         while ((ze = zis.getNextEntry()) != null) {
152         String JavaDoc name = ze.getName();
153 // if (name.charAt(0) == '/')
154
// name = name.substring(1);
155
if (zipDirs[nr] != null) {
156             if (!name.startsWith(zipDirs[nr]))
157             continue;
158             name = name.substring(zipDirs[nr].length());
159         }
160         if (!ze.isDirectory() && name.endsWith(".class"))
161             addEntry(zipEntries[nr], name);
162         zis.closeEntry();
163         }
164         zis.close();
165     } catch (IOException JavaDoc ex) {
166         GlobalOptions.err.println("Remote zip file "+bases[nr]
167                       +" is corrupted.");
168         // disable entry
169
bases[nr] = null;
170         urlzips[nr] = null;
171         zipEntries[nr] = null;
172         return;
173     }
174     }
175     
176     /**
177      * Creates a new search path for the given path.
178      * @param path The path where we should search for files. They
179      * should be separated by the system dependent pathSeparator. The
180      * entries may also be zip or jar files.
181      */

182     public SearchPath(String JavaDoc path) {
183     // Calculate a good approximation (rounded upwards) of the tokens
184
// in this path.
185
int length = 1;
186     for (int index=path.indexOf(File.pathSeparatorChar);
187          index != -1; length++)
188         index = path.indexOf(File.pathSeparatorChar, index+1);
189     if (File.pathSeparatorChar != altPathSeparatorChar) {
190         for (int index=path.indexOf(altPathSeparatorChar);
191          index != -1; length++)
192         index = path.indexOf(altPathSeparatorChar, index+1);
193     }
194     bases = new URL JavaDoc[length];
195     urlzips = new byte[length][];
196         dirs = new File JavaDoc[length];
197         zips = new ZipFile JavaDoc[length];
198         zipEntries = new Hashtable JavaDoc[length];
199         zipDirs = new String JavaDoc[length];
200     int i = 0;
201         for (int ptr=0; ptr < path.length(); ptr++, i++) {
202         int next = ptr;
203         while (next < path.length()
204            && path.charAt(next) != File.pathSeparatorChar
205            && path.charAt(next) != altPathSeparatorChar)
206         next++;
207
208         int index = ptr;
209     colon_separator:
210         while (next > ptr
211            && next < path.length()
212            && path.charAt(next) == ':') {
213         // Check if this is a URL instead of a pathSeparator
214
// Since this is a while loop it allows nested urls like
215
// jar:ftp://ftp.foo.org/pub/foo.jar!/
216

217         while (index < next) {
218             char c = path.charAt(index);
219             // According to RFC 1738 letters, digits, '+', '-'
220
// and '.' are allowed SCHEMA characters. We
221
// disallow '.' because it is a good marker that
222
// the user has specified a filename instead of a
223
// URL.
224
if ((c < 'A' || c > 'Z')
225             && (c < 'a' || c > 'z')
226             && (c < '0' || c > '9')
227             && "+-".indexOf(c) == -1) {
228             break colon_separator;
229             }
230             index++;
231         }
232         next++;
233         index++;
234         while (next < path.length()
235                && path.charAt(next) != File.pathSeparatorChar
236                && path.charAt(next) != altPathSeparatorChar)
237             next++;
238         }
239         String JavaDoc token = path.substring(ptr, next);
240         ptr = next;
241
242         boolean mustBeJar = false;
243         // We handle jar URL's ourself.
244
if (token.startsWith("jar:")) {
245         index = 0;
246         do {
247             index = token.indexOf('!', index);
248         } while (index != -1 && index != token.length()-1
249              && token.charAt(index+1) != '/');
250
251         if (index == -1 || index == token.length()-1) {
252             GlobalOptions.err.println("Warning: Illegal jar url "
253                           + token + ".");
254             continue;
255         }
256         zipDirs[i] = token.substring(index+2);
257         if (!zipDirs[i].endsWith("/"))
258             zipDirs[i] = zipDirs[i] + "/";
259         token = token.substring(4, index);
260         mustBeJar = true;
261         }
262         index = token.indexOf(':');
263         if (index != -1 && index < token.length()-2
264         && token.charAt(index+1) == '/'
265         && token.charAt(index+2) == '/') {
266         // This looks like an URL.
267
try {
268             bases[i] = new URL JavaDoc(token);
269             try {
270             URLConnection JavaDoc connection = bases[i].openConnection();
271             if (mustBeJar
272                 || token.endsWith(".zip") || token.endsWith(".jar")
273                 || connection.getContentType().endsWith("/zip")) {
274                 // This is a zip file. Read it into memory.
275
readURLZip(i, connection);
276             }
277             } catch (IOException JavaDoc ex) {
278             // ignore
279
} catch (SecurityException JavaDoc ex) {
280             GlobalOptions.err.println
281                 ("Warning: Security exception while accessing "
282                  + bases[i] + ".");
283             }
284         } catch (MalformedURLException JavaDoc ex) {
285             /* disable entry */
286             bases[i] = null;
287             dirs[i] = null;
288         }
289         } else {
290         try {
291             dirs[i] = new File JavaDoc(token);
292             if (mustBeJar || !dirs[i].isDirectory()) {
293             try {
294                 zips[i] = new ZipFile JavaDoc(dirs[i]);
295             } catch (java.io.IOException JavaDoc ex) {
296                 /* disable this entry */
297                 dirs[i] = null;
298             }
299             }
300         } catch (SecurityException JavaDoc ex) {
301             /* disable this entry */
302             GlobalOptions.err.println
303             ("Warning: SecurityException while accessing "
304              + token + ".");
305             dirs[i] = null;
306         }
307         }
308     }
309     }
310
311     public boolean exists(String JavaDoc filename) {
312     String JavaDoc localFileName =
313         (java.io.File.separatorChar != '/')
314         ? filename.replace('/', java.io.File.separatorChar)
315         : filename;
316         for (int i=0; i<dirs.length; i++) {
317         if (zipEntries[i] != null) {
318         if (zipEntries[i].get(filename) != null)
319             return true;
320
321         String JavaDoc dir = "";
322         String JavaDoc name = filename;
323         int index = filename.lastIndexOf('/');
324         if (index >= 0) {
325             dir = filename.substring(0, index);
326             name = filename.substring(index+1);
327         }
328         Vector JavaDoc directory = (Vector JavaDoc)zipEntries[i].get(dir);
329         if (directory != null && directory.contains(name))
330             return true;
331         continue;
332         }
333         if (bases[i] != null) {
334         try {
335             URL JavaDoc url = new URL JavaDoc(bases[i], filename);
336             URLConnection JavaDoc conn = url.openConnection();
337             conn.connect();
338             conn.getInputStream().close();
339             return true;
340         } catch (IOException JavaDoc ex) {
341             /* ignore */
342         }
343         continue;
344         }
345             if (dirs[i] == null)
346                 continue;
347             if (zips[i] != null) {
348         String JavaDoc fullname = zipDirs[i] != null
349             ? zipDirs[i] + filename : filename;
350                 ZipEntry JavaDoc ze = zips[i].getEntry(fullname);
351                 if (ze != null)
352                     return true;
353             } else {
354         try {
355             File JavaDoc f = new File JavaDoc(dirs[i], localFileName);
356             if (f.exists())
357             return true;
358         } catch (SecurityException JavaDoc ex) {
359             /* ignore and take next element */
360         }
361             }
362         }
363         return false;
364     }
365
366     /**
367      * Searches for a file in the search path.
368      * @param filename the filename. The path components should be separated
369      * by <code>/</code>.
370      * @return An InputStream for the file.
371      */

372     public InputStream JavaDoc getFile(String JavaDoc filename) throws IOException JavaDoc {
373     String JavaDoc localFileName =
374         (java.io.File.separatorChar != '/')
375         ? filename.replace('/', java.io.File.separatorChar)
376         : filename;
377         for (int i=0; i<dirs.length; i++) {
378         if (urlzips[i] != null) {
379         ZipInputStream JavaDoc zis = new ZipInputStream JavaDoc
380             (new ByteArrayInputStream JavaDoc(urlzips[i]));
381         ZipEntry JavaDoc ze;
382         String JavaDoc fullname = zipDirs[i] != null
383             ? zipDirs[i] + filename : filename;
384         while ((ze = zis.getNextEntry()) != null) {
385             if (ze.getName().equals(fullname)) {
386 ///#ifdef JDK11
387
/// // The skip method in jdk1.1.7 ZipInputStream
388
/// // is buggy. We return a wrapper that fixes
389
/// // this.
390
/// return new FilterInputStream(zis) {
391
/// private byte[] tmpbuf = new byte[512];
392
/// public long skip(long n) throws IOException {
393
/// long skipped = 0;
394
/// while (n > 0) {
395
/// int count = read(tmpbuf, 0,
396
/// (int)Math.min(n, 512L));
397
/// if (count == -1)
398
/// return skipped;
399
/// skipped += count;
400
/// n -= count;
401
/// }
402
/// return skipped;
403
/// }
404
/// };
405
///#else
406
return zis;
407 ///#endif
408
}
409             zis.closeEntry();
410         }
411         continue;
412         }
413         if (bases[i] != null) {
414         try {
415             URL JavaDoc url = new URL JavaDoc(bases[i], filename);
416             URLConnection JavaDoc conn = url.openConnection();
417             conn.setAllowUserInteraction(true);
418             return conn.getInputStream();
419         } catch (SecurityException JavaDoc ex) {
420             GlobalOptions.err.println("Warning: SecurityException"
421                           + " while accessing "
422                           + bases[i] + filename);
423             ex.printStackTrace(GlobalOptions.err);
424             /* ignore and take next element */
425         } catch (FileNotFoundException JavaDoc ex) {
426             /* ignore and take next element */
427         }
428         continue;
429         }
430             if (dirs[i] == null)
431                 continue;
432             if (zips[i] != null) {
433         String JavaDoc fullname = zipDirs[i] != null
434             ? zipDirs[i] + filename : filename;
435                 ZipEntry JavaDoc ze = zips[i].getEntry(fullname);
436                 if (ze != null)
437                     return zips[i].getInputStream(ze);
438             } else {
439         try {
440             File JavaDoc f = new File JavaDoc(dirs[i], localFileName);
441             if (f.exists())
442             return new FileInputStream JavaDoc(f);
443         } catch (SecurityException JavaDoc ex) {
444             GlobalOptions.err.println("Warning: SecurityException"
445                           + " while accessing "
446                           + dirs[i] + localFileName);
447             /* ignore and take next element */
448         }
449             }
450         }
451         throw new FileNotFoundException JavaDoc(filename);
452     }
453
454     /**
455      * Searches for a filename in the search path and tells if it is a
456      * directory.
457      * @param filename the filename. The path components should be separated
458      * by <code>/</code>.
459      * @return true, if filename exists and is a directory, false otherwise.
460      */

461     public boolean isDirectory(String JavaDoc filename) {
462     String JavaDoc localFileName =
463         (java.io.File.separatorChar != '/')
464         ? filename.replace('/', java.io.File.separatorChar)
465         : filename;
466         for (int i=0; i<dirs.length; i++) {
467             if (dirs[i] == null)
468                 continue;
469             if (zips[i] != null && zipEntries[i] == null)
470         fillZipEntries(i);
471
472         if (zipEntries[i] != null) {
473         if (zipEntries[i].containsKey(filename))
474             return true;
475             } else {
476         try {
477             File JavaDoc f = new File JavaDoc(dirs[i], localFileName);
478             if (f.exists())
479             return f.isDirectory();
480         } catch (SecurityException JavaDoc ex) {
481             GlobalOptions.err.println("Warning: SecurityException"
482                           + " while accessing "
483                           + dirs[i] + localFileName);
484         }
485             }
486         }
487         return false;
488     }
489
490     /**
491      * Searches for all files in the given directory.
492      * @param dirName the directory name. The path components should
493      * be separated by <code>/</code>.
494      * @return An enumeration with all files/directories in the given
495      * directory. */

496     public Enumeration JavaDoc listFiles(final String JavaDoc dirName) {
497         return new Enumeration JavaDoc() {
498             int pathNr;
499             Enumeration JavaDoc zipEnum;
500             int fileNr;
501         String JavaDoc localDirName =
502         (java.io.File.separatorChar != '/')
503         ? dirName.replace('/', java.io.File.separatorChar)
504         : dirName;
505         File JavaDoc currentDir;
506             String JavaDoc[] files;
507
508             public String JavaDoc findNextFile() {
509                 while (true) {
510                     if (zipEnum != null) {
511                         while (zipEnum.hasMoreElements()) {
512                 return (String JavaDoc) zipEnum.nextElement();
513                         }
514                         zipEnum = null;
515                     }
516                     if (files != null) {
517                         while (fileNr < files.length) {
518                             String JavaDoc name = files[fileNr++];
519                             if (name.endsWith(".class")) {
520                                 return name;
521                             } else if (name.indexOf(".") == -1) {
522                 /* ignore directories containing a dot.
523                  * they can't be a package directory.
524                  */

525                 File JavaDoc f = new File JavaDoc(currentDir, name);
526                 if (f.exists() && f.isDirectory())
527                     return name;
528                 }
529                         }
530                         files = null;
531                     }
532                     if (pathNr == dirs.length)
533                         return null;
534
535                     if (zips[pathNr] != null && zipEntries[pathNr] == null)
536             fillZipEntries(pathNr);
537
538             if (zipEntries[pathNr] != null) {
539             Vector JavaDoc entries =
540                 (Vector JavaDoc) zipEntries[pathNr].get(dirName);
541             if (entries != null)
542                 zipEnum = entries.elements();
543                     } else if (dirs[pathNr] != null) {
544             try {
545                 File JavaDoc f = new File JavaDoc(dirs[pathNr], localDirName);
546                 if (f.exists() && f.isDirectory()) {
547                 currentDir = f;
548                 files = f.list();
549                 fileNr = 0;
550                 }
551             } catch (SecurityException JavaDoc ex) {
552                 GlobalOptions.err.println
553                     ("Warning: SecurityException"
554                  + " while accessing "
555                  + dirs[pathNr] + localDirName);
556                 /* ignore and take next element */
557             }
558                     }
559                     pathNr++;
560                 }
561             }
562
563             String JavaDoc nextName;
564
565             public boolean hasMoreElements() {
566                 return (nextName != null
567                         || (nextName = findNextFile()) != null);
568             }
569
570             public Object JavaDoc nextElement() {
571                 if (nextName == null)
572                     return findNextFile();
573                 else {
574                     String JavaDoc result = nextName;
575                     nextName = null;
576                     return result;
577                 }
578             }
579         };
580     }
581 }
582
Popular Tags