1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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 }