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