View Javadoc

1   /**
2    * Copyright 2007 Google Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.tonicsystems.jarjar.util;
18  
19  import java.util.*;
20  import java.util.zip.*;
21  import java.io.*;
22  import java.util.jar.*;
23  
24  public class ClassPathIterator implements Iterator<ClassPathEntry>
25  {
26      private static final FileFilter CLASS_FILTER = new FileFilter() {
27          public boolean accept(File file) {
28              return file.isDirectory() || isClass(file.getName());
29          }
30      };
31  
32      private static final FileFilter JAR_FILTER = new FileFilter() {
33          public boolean accept(File file) {
34              return hasExtension(file.getName(), ".jar");
35          }
36      };
37      
38      private final Iterator<File> files;
39      private Iterator<ClassPathEntry> entries = Collections.<ClassPathEntry>emptyList().iterator();
40      private ClassPathEntry next;
41      private List<ZipFile> zips = new ArrayList<ZipFile>();
42  
43      public ClassPathIterator(String classPath) throws IOException {
44          this(new File(System.getProperty("user.dir")), classPath, null);
45      }
46      
47      public ClassPathIterator(File parent, String classPath, String delim) throws IOException {
48          if (delim == null) {
49              delim = System.getProperty("path.separator");
50          }
51          StringTokenizer st = new StringTokenizer(classPath, delim);
52          List<File> fileList = new ArrayList<File>();
53          while (st.hasMoreTokens()) {
54              String part = (String)st.nextElement();
55              boolean wildcard = false;
56              if (part.endsWith("/*")) {
57                  part = part.substring(0, part.length() - 1);
58                  if (part.indexOf('*') >= 0)
59                      throw new IllegalArgumentException("Multiple wildcards are not allowed: " + part);
60                  wildcard = true;
61              } else if (part.indexOf('*') >= 0) {
62                  throw new IllegalArgumentException("Incorrect wildcard usage: " + part);
63              }
64                  
65              File file = new File(part);
66              if (!file.isAbsolute())
67                  file = new File(parent, part);
68              if (!file.exists())
69                  throw new IllegalArgumentException("File " + file + " does not exist");
70  
71              if (wildcard) {
72                  if (!file.isDirectory())
73                      throw new IllegalArgumentException("File " + file + " + is not a directory");
74                  fileList.addAll(findFiles(file, JAR_FILTER, false, new ArrayList<File>()));
75              } else {
76                  fileList.add(file);
77              }
78          }
79          this.files = fileList.iterator();
80          advance();
81      }
82  
83      public boolean hasNext() {
84          return next != null;
85      }
86  
87      /** Closes all zip files opened by this iterator. */
88      public void close() throws IOException {
89        next = null;
90        for (ZipFile zip : zips) {
91          zip.close();
92        }
93      }
94  
95      public void remove() {
96          throw new UnsupportedOperationException();
97      }
98  
99      public ClassPathEntry next() {
100         if (!hasNext())
101             throw new NoSuchElementException();
102         ClassPathEntry result = next;
103         try {
104             advance();
105         } catch (IOException e) {
106             throw new RuntimeIOException(e);
107         }
108         return result;
109     }
110 
111     private void advance() throws IOException {
112         if (!entries.hasNext()) {
113             if (!files.hasNext()) {
114                 next = null;
115                 return;
116             }
117             File file = files.next();
118             if (hasExtension(file.getName(), ".jar")) {
119                 ZipFile zip = new JarFile(file);
120                 zips.add(zip);
121                 entries = new ZipIterator(zip);
122             } else if (hasExtension(file.getName(), ".zip")) {
123                 ZipFile zip = new ZipFile(file);
124                 zips.add(zip);
125                 entries = new ZipIterator(zip);
126             } else if (file.isDirectory()) {
127                 entries = new FileIterator(file);
128             } else {
129                 throw new IllegalArgumentException("Do not know how to handle " + file);
130             }
131         }
132 
133         boolean foundClass = false;
134         while (!foundClass && entries.hasNext()) {
135           next = entries.next();
136           foundClass = isClass(next.getName());
137         }
138         if (!foundClass) {
139           advance();
140         }
141     }
142 
143     private static class ZipIterator implements Iterator<ClassPathEntry> {
144       private final ZipFile zip;
145       private final Enumeration<? extends ZipEntry> entries;
146 
147       ZipIterator(ZipFile zip) {
148         this.zip = zip;
149         this.entries = zip.entries();
150       }
151 
152       public boolean hasNext() {
153         return entries.hasMoreElements();
154       }
155 
156       public void remove() {
157         throw new UnsupportedOperationException();
158       }
159 
160       public ClassPathEntry next() {
161         final ZipEntry entry = entries.nextElement();
162         return new ClassPathEntry() {
163           public String getSource() {
164             return zip.getName();
165           }
166 
167           public String getName() {
168             return entry.getName();
169           }
170 
171           public InputStream openStream() throws IOException {
172             return zip.getInputStream(entry);
173           }
174         };
175       }
176     }
177 
178     private static class FileIterator implements Iterator<ClassPathEntry> {
179       private final File dir;
180       private final Iterator<File> entries;
181 
182       FileIterator(File dir) {
183         this.dir = dir;
184         this.entries = findFiles(dir, CLASS_FILTER, true, new ArrayList<File>()).iterator();
185       }
186 
187       public boolean hasNext() {
188         return entries.hasNext();
189       }
190 
191       public void remove() {
192         throw new UnsupportedOperationException();
193       }
194       
195       public ClassPathEntry next() {
196         final File file = entries.next();
197         return new ClassPathEntry() {
198           public String getSource() throws IOException {
199             return dir.getCanonicalPath();
200           }
201 
202           public String getName() {
203             return file.getName();
204           }
205 
206           public InputStream openStream() throws IOException {
207             return new BufferedInputStream(new FileInputStream(file));
208           }
209         };
210       }
211     }
212 
213     private static List<File> findFiles(File dir, FileFilter filter, boolean recurse, List<File> collect) {
214         for (File file : dir.listFiles(filter)) {
215             if (recurse && file.isDirectory()) {
216                 findFiles(file, filter, recurse, collect);
217             } else {
218                 collect.add(file);
219             }
220         }
221         return collect;
222     }
223 
224     private static boolean isClass(String name) {
225         return hasExtension(name, ".class");
226     }
227 
228     private static boolean hasExtension(String name, String ext) {
229         if (name.length() <  ext.length())
230             return false;
231         String actual = name.substring(name.length() - ext.length());
232         return actual.equals(ext) || actual.equals(ext.toUpperCase());
233     }
234 }