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