| /******************************************************************************* |
| * Copyright (c) 2005, 2012 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0, which accompanies this distribution |
| * and is available at http://www.eclipse.org/legal/epl-v10.html. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.common.utility.internal; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| import org.eclipse.jpt.common.utility.Filter; |
| import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable; |
| import org.eclipse.jpt.common.utility.internal.iterators.ArrayIterator; |
| import org.eclipse.jpt.common.utility.internal.iterators.CompositeIterator; |
| import org.eclipse.jpt.common.utility.internal.iterators.EmptyIterator; |
| import org.eclipse.jpt.common.utility.internal.iterators.FilteringIterator; |
| import org.eclipse.jpt.common.utility.internal.iterators.TransformationIterator; |
| |
| /** |
| * <code>Classpath</code> models a Java classpath, which consists of a list of |
| * {@link Entry}s, each of which contain Java classes. The classpath can return |
| * the names of classes found in it etc. There are a number of static |
| * convenience methods that can be use to construct <code>Classpath</code>s |
| * corresponding to the Java classpath etc. |
| */ |
| public class Classpath |
| implements Serializable |
| { |
| /** The entries in the classpath */ |
| private final Entry[] entries; |
| |
| private static final long serialVersionUID = 1L; |
| |
| |
| // ********** static methods ********** |
| |
| // ***** factory methods for "standard" classpaths ***** |
| |
| /** |
| * Return the Java "boot" classpath. This includes <code>rt.jar</code>. |
| */ |
| public static Classpath bootClasspath() { |
| return new Classpath(System.getProperty("sun.boot.class.path")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Return a "virtual classpath" that contains all the jars |
| * that would be used by the Java Extension Mechanism. |
| */ |
| public static Classpath javaExtensionClasspath() { |
| File[] dirs = javaExtensionDirectories(); |
| List<String> jarFileNames = new ArrayList<String>(); |
| for (File dir : dirs) { |
| if (dir.isDirectory()) { |
| addJarFileNamesTo(dir, jarFileNames); |
| } |
| } |
| return new Classpath(jarFileNames); |
| } |
| |
| /** |
| * Return the Java "system" classpath. |
| */ |
| public static Classpath javaClasspath() { |
| return new Classpath(System.getProperty("java.class.path")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Return the unretouched "complete" classpath. |
| * This includes the boot classpath, the Java Extension |
| * Mechanism classpath, and the normal "system" classpath. |
| */ |
| public static Classpath completeClasspath() { |
| return new Classpath(new Classpath[] { |
| bootClasspath(), |
| javaExtensionClasspath(), |
| javaClasspath() |
| }); |
| } |
| |
| /** |
| * Return a classpath that contains the location of the specified class. |
| */ |
| public static Classpath classpathFor(Class<?> javaClass) { |
| return new Classpath(locationFor(javaClass)); |
| } |
| |
| |
| // ***** file => class ***** |
| |
| /** |
| * Convert a relative file name to a class name; this will work for |
| * any file that has a single extension beyond the base |
| * class name:<ul> |
| * <li><code>"java/lang/String.class"</code> is converted to <code>"java.lang.String"</code> |
| * <li><code>"java/lang/String.java"</code> is converted to <code>"java.lang.String"</code> |
| * </ul> |
| */ |
| public static String convertToClassName(String classFileName) { |
| String className = FileTools.stripExtension(classFileName); |
| // do this for archive entry names |
| className = className.replace('/', '.'); |
| // do this for O/S-specific file names |
| if (File.separatorChar != '/') { |
| className = className.replace(File.separatorChar, '.'); |
| } |
| return className; |
| } |
| |
| /** |
| * Convert a file to a class name; |
| * e.g. <code>File(java/lang/String.class)</code> is converted to |
| * <code>"java.lang.String"</code>. |
| */ |
| public static String convertToClassName(File classFile) { |
| return convertToClassName(classFile.getPath()); |
| } |
| |
| /** |
| * Convert a relative file name to a class; |
| * e.g. <code>"java/lang/String.class"</code> is converted to |
| * <code>java.lang.String.class</code>. |
| */ |
| public static Class<?> convertToClass(String classFileName) throws ClassNotFoundException { |
| return Class.forName(convertToClassName(classFileName)); |
| } |
| |
| /** |
| * Convert a relative file to a class; |
| * e.g. <code>File(java/lang/String.class)</code> is converted to |
| * <code>java.lang.String.class</code>. |
| */ |
| public static Class<?> convertToClass(File classFile) throws ClassNotFoundException { |
| return convertToClass(classFile.getPath()); |
| } |
| |
| |
| // ***** class => JAR entry ***** |
| |
| /** |
| * Convert a class name to an archive entry name base; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>"java/lang/String"</code>. |
| */ |
| public static String convertToArchiveEntryNameBase(String className) { |
| return className.replace('.', '/'); |
| } |
| |
| /** |
| * Convert a class to an archive entry name base; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"java/lang/String"</code>. |
| */ |
| public static String convertToArchiveEntryNameBase(Class<?> javaClass) { |
| return convertToArchiveEntryNameBase(javaClass.getName()); |
| } |
| |
| /** |
| * Convert a class name to an archive class file entry name; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>"java/lang/String.class"</code>. |
| */ |
| public static String convertToArchiveClassFileEntryName(String className) { |
| return convertToArchiveEntryNameBase(className) + ".class"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Convert a class to an archive class file entry name; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"java/lang/String.class"</code>. |
| */ |
| public static String convertToArchiveClassFileEntryName(Class<?> javaClass) { |
| return convertToArchiveClassFileEntryName(javaClass.getName()); |
| } |
| |
| |
| // ***** class => file (.class or .java) ***** |
| |
| /** |
| * Convert a class name to a file name base for the current O/S; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>"java/lang/String"</code> on Unix and |
| * <code>"java\\lang\\String"</code> on Windows. |
| */ |
| public static String convertToFileNameBase(String className) { |
| return className.replace('.', File.separatorChar); |
| } |
| |
| /** |
| * Convert a class to a file name base for the current O/S; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"java/lang/String"</code> on Unix and |
| * <code>"java\\lang\\String"</code> on Windows. |
| */ |
| public static String convertToFileNameBase(Class<?> javaClass) { |
| return convertToFileNameBase(javaClass.getName()); |
| } |
| |
| /** |
| * Convert a class name to a class file name for the current O/S; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>"java/lang/String.class"</code> on Unix and |
| * <code>"java\\lang\\String.class"</code> on Windows. |
| */ |
| public static String convertToClassFileName(String className) { |
| return convertToFileNameBase(className) + ".class"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Convert a class to a class file name for the current O/S; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"java/lang/String.class"</code> on Unix and |
| * <code>"java\\lang\\String.class"</code> on Windows. |
| */ |
| public static String convertToClassFileName(Class<?> javaClass) { |
| return convertToClassFileName(javaClass.getName()); |
| } |
| |
| /** |
| * Convert a class name to a class file for the current O/S; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>File(java/lang/String.class)</code>. |
| */ |
| public static File convertToClassFile(String className) { |
| return new File(convertToClassFileName(className)); |
| } |
| |
| /** |
| * Convert a class to a class file for the current O/S; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>File(java/lang/String.class)</code>. |
| */ |
| public static File convertToClassFile(Class<?> javaClass) { |
| return convertToClassFile(javaClass.getName()); |
| } |
| |
| /** |
| * Convert a class name to a java file name for the current O/S; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>"java/lang/String.java"</code> on Unixl and |
| * <code>"java\\lang\\String.java"</code> on Windows. |
| */ |
| public static String convertToJavaFileName(String className) { |
| return convertToFileNameBase(className) + ".java"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Convert a class to a java file name for the current O/S; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"java/lang/String.java"</code> on Unix and |
| * <code>"java\\lang\\String.java"</code> on Windows. |
| */ |
| public static String convertToJavaFileName(Class<?> javaClass) { |
| return convertToJavaFileName(javaClass.getName()); |
| } |
| |
| /** |
| * Convert a class name to a java file for the current O/S; |
| * e.g. <code>"java.lang.String"</code> is converted to |
| * <code>File(java/lang/String.java)</code>. |
| */ |
| public static File convertToJavaFile(String className) { |
| return new File(convertToJavaFileName(className)); |
| } |
| |
| /** |
| * Convert a class to a java file for the current O/S; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>File(java/lang/String.java)</code>. |
| */ |
| public static File convertToJavaFile(Class<?> javaClass) { |
| return convertToJavaFile(javaClass.getName()); |
| } |
| |
| |
| // ***** class => resource ***** |
| |
| /** |
| * Convert a class to a resource name; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>"/java/lang/String.class"</code>. |
| */ |
| public static String convertToResourceName(Class<?> javaClass) { |
| return '/' + convertToArchiveClassFileEntryName(javaClass); |
| } |
| |
| /** |
| * Convert a class to a resource; |
| * e.g. <code>java.lang.String.class</code> is converted to |
| * <code>URL(jar:file:/C:/jdk/1.4.2_04/jre/lib/rt.jar!/java/lang/String.class)</code>. |
| */ |
| public static URL convertToResource(Class<?> javaClass) { |
| return javaClass.getResource(convertToResourceName(javaClass)); |
| } |
| |
| |
| // ***** utilities ***** |
| |
| /** |
| * Return whether the specified file is an archive file; |
| * i.e. its name ends with <code>".zip"</code> or <code>".jar"</code>. |
| */ |
| public static boolean fileNameIsArchive(String fileName) { |
| String ext = FileTools.extension(fileName).toLowerCase(); |
| return ext.equals(".jar") || ext.equals(".zip"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * Return whether the specified file is an archive file; |
| * i.e. its name ends with <code>".zip"</code> or <code>".jar"</code>. |
| */ |
| public static boolean fileIsArchive(File file) { |
| return fileNameIsArchive(file.getName()); |
| } |
| |
| /** |
| * Return what should be the fully-qualified file name |
| * for the JRE runtime JAR; |
| * e.g. <code>"C:\jdk1.4.2_04\jre\lib\rt.jar"</code>. |
| */ |
| public static String rtJarName() { |
| return locationFor(java.lang.Object.class); |
| } |
| |
| /** |
| * Return the location from where the specified class was loaded. |
| */ |
| public static String locationFor(Class<?> javaClass) { |
| URL url = convertToResource(javaClass); |
| String path; |
| try { |
| path = FileTools.buildFile(url).getPath(); |
| } catch (URISyntaxException ex) { |
| throw new RuntimeException(ex); |
| } |
| String protocol = url.getProtocol().toLowerCase(); |
| if (protocol.equals("jar")) { //$NON-NLS-1$ |
| // if the class is in a JAR, the URL will look something like this: |
| // jar:file:/C:/jdk/1.4.2_04/jre/lib/rt.jar!/java/lang/String.class |
| return path.substring(0, path.indexOf('!')); |
| } else if (protocol.equals("file")) { //$NON-NLS-1$ |
| // if the class is in a directory, the URL will look something like this: |
| // file:/C:/dev/main/mwdev/class/org/eclipse/dali/utility/Classpath.class |
| return path.substring(0, path.length() - convertToClassFileName(javaClass).length() - 1); |
| } else if (protocol.equals("bundleresource")) { //$NON-NLS-1$ |
| // if the class is in a bundle resource (Eclipse?), the URL will look something like this: |
| // bundleresource://43/org/eclipse/dali/utility/Classpath.class |
| return path.substring(0, path.length() - convertToClassFileName(javaClass).length() - 1); |
| } |
| |
| throw new IllegalStateException(url.toString()); |
| } |
| |
| /** |
| * Return the directories used by the Java Extension Mechanism. |
| */ |
| public static File[] javaExtensionDirectories() { |
| return convertToFiles(javaExtensionDirectoryNames()); |
| } |
| |
| /** |
| * Return the directory names used by the Java Extension Mechanism. |
| */ |
| public static String[] javaExtensionDirectoryNames() { |
| return System.getProperty("java.ext.dirs").split(File.pathSeparator); //$NON-NLS-1$ |
| } |
| |
| |
| // ***** internal ***** |
| |
| private static File[] convertToFiles(String[] fileNames) { |
| File[] files = new File[fileNames.length]; |
| for (int i = fileNames.length; i-- > 0; ) { |
| files[i] = new File(fileNames[i]); |
| } |
| return files; |
| } |
| |
| private static void addJarFileNamesTo(File dir, List<String> jarFileNames) { |
| File[] jarFiles = jarFilesIn(dir); |
| for (File jarFile : jarFiles) { |
| jarFileNames.add(FileTools.canonicalFile(jarFile).getPath()); |
| } |
| } |
| |
| private static File[] jarFilesIn(File directory) { |
| return directory.listFiles(jarFileFilter()); |
| } |
| |
| private static FileFilter jarFileFilter() { |
| return new FileFilter() { |
| public boolean accept(File file) { |
| return FileTools.extension(file.getName()).toLowerCase().equals(".jar"); //$NON-NLS-1$ |
| } |
| }; |
| } |
| |
| |
| // ********** constructors ********** |
| |
| /** |
| * Construct a classpath with the specified entries. |
| */ |
| private Classpath(Entry[] entries) { |
| super(); |
| this.entries = entries; |
| } |
| |
| /** |
| * Construct a classpath with the specified entries. |
| */ |
| public Classpath(String... fileNames) { |
| this(buildEntries(fileNames)); |
| } |
| |
| /** |
| * Skip empty file names because they will end up expanding to the current |
| * working directory, which is not what we want. Empty file names actually |
| * occur with some frequency; such as when the classpath has been built up |
| * dynamically with too many separators. For example:<pre> |
| * "C:\dev\foo.jar;;C:\dev\bar.jar" |
| * </pre>will be parsed into three file names:<pre> |
| * { "C:\dev\foo.jar", "", "C:\dev\bar.jar" } |
| * </pre> |
| */ |
| private static Entry[] buildEntries(String[] fileNames) { |
| List<Entry> entries = new ArrayList<Entry>(); |
| for (String fileName : fileNames) { |
| if ((fileName != null) && (fileName.length() != 0)) { |
| entries.add(new Entry(fileName)); |
| } |
| } |
| return entries.toArray(new Entry[entries.size()]); |
| } |
| |
| /** |
| * Construct a classpath with the specified path. |
| */ |
| public Classpath(String path) { |
| this(path.split(File.pathSeparator)); |
| } |
| |
| /** |
| * Construct a classpath with the specified entries. |
| */ |
| public Classpath(Iterable<String> fileNames) { |
| this(ArrayTools.array(fileNames, StringTools.EMPTY_STRING_ARRAY)); |
| } |
| |
| /** |
| * Consolidate the specified classpaths into a single classpath. |
| */ |
| public Classpath(Classpath... classpaths) { |
| this(consolidateEntries(classpaths)); |
| } |
| |
| private static Entry[] consolidateEntries(Classpath[] classpaths) { |
| List<Entry> entries = new ArrayList<Entry>(); |
| for (Classpath classpath : classpaths) { |
| CollectionTools.addAll(entries, classpath.getEntries()); |
| } |
| return entries.toArray(new Entry[entries.size()]); |
| } |
| |
| |
| // ********** public API ********** |
| |
| /** |
| * Return the classpath's entries. |
| */ |
| public Iterable<Entry> getEntries() { |
| return new ArrayIterable<Entry>(this.entries); |
| } |
| |
| /** |
| * Return the classpath's path. |
| */ |
| public String getPath() { |
| int max = this.entries.length - 1; |
| if (max == -1) { |
| return ""; //$NON-NLS-1$ |
| } |
| StringBuilder sb = new StringBuilder(2000); |
| // stop one short of the end of the array |
| for (int i = 0; i < max; i++) { |
| sb.append(this.entries[i].getFileName()); |
| sb.append(File.pathSeparatorChar); |
| } |
| sb.append(this.entries[max].getFileName()); |
| return sb.toString(); |
| } |
| |
| /** |
| * Search the classpath for the specified (unqualified) file |
| * and return its entry. Return null if an entry is not found. |
| * For example, you could use this method to find the entry |
| * for <code>"rt.jar"</code> or <code>"toplink.jar"</code>. |
| */ |
| public Entry getEntryForFileNamed(String shortFileName) { |
| for (Entry entry : this.entries) { |
| if (entry.getFile().getName().equals(shortFileName)) { |
| return entry; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the first entry file in the classpath |
| * that contains the specified class. |
| * Return null if an entry is not found. |
| */ |
| public Entry getEntryForClassNamed(String className) { |
| String relativeClassFileName = convertToClassFileName(className); |
| String archiveEntryName = convertToArchiveClassFileEntryName(className); |
| for (Entry entry : this.entries) { |
| if (entry.contains(relativeClassFileName, archiveEntryName)) { |
| return entry; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath, |
| * with duplicates removed. |
| * @see #classNames() |
| */ |
| public Iterable<String> getClassNames() { |
| return this.getClassNames(Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath |
| * and accepted by the specified filter, with duplicates removed. |
| * @see #classNames(Filter) |
| */ |
| public Iterable<String> getClassNames(Filter<String> filter) { |
| Collection<String> classNames = new HashSet<String>(10000); |
| this.addClassNamesTo(classNames, filter); |
| return classNames; |
| } |
| |
| /** |
| * Add the names of all the classes discovered on the classpath |
| * to the specified collection. |
| */ |
| public void addClassNamesTo(Collection<String> classNames) { |
| this.addClassNamesTo(classNames, Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Add the names of all the classes discovered on the classpath |
| * and accepted by the specified filter to the specified collection. |
| */ |
| public void addClassNamesTo(Collection<String> classNames, Filter<String> filter) { |
| for (Entry entry : this.entries) { |
| entry.addClassNamesTo(classNames, filter); |
| } |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath. |
| * Just a bit more performant than {@link #getClassNames()}. |
| */ |
| public Iterator<String> classNames() { |
| return this.classNames(Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath |
| * that are accepted by the specified filter. |
| * Just a bit more performant than {@link #getClassNames(Filter)}. |
| */ |
| public Iterator<String> classNames(Filter<String> filter) { |
| return new CompositeIterator<String>(this.entryClassNamesIterators(filter)); |
| } |
| |
| private Iterator<Iterator<String>> entryClassNamesIterators(final Filter<String> filter) { |
| return new TransformationIterator<Entry, Iterator<String>>(new ArrayIterator<Entry>(this.entries)) { |
| @Override |
| protected Iterator<String> transform(Entry entry) { |
| return entry.classNames(filter); |
| } |
| }; |
| } |
| |
| /** |
| * Return a "compressed" version of the classpath with its |
| * duplicate entries eliminated. |
| */ |
| public Classpath compressed() { |
| return new Classpath(ArrayTools.removeDuplicateElements(this.entries)); |
| } |
| |
| /** |
| * Convert the classpath to an array of URLs |
| * (that can be used to instantiate a {@link java.net.URLClassLoader}). |
| */ |
| public Iterable<URL> getURLs() { |
| int len = this.entries.length; |
| URL[] urls = new URL[len]; |
| for (int i = 0; i < len; i++) { |
| urls[i] = this.entries[i].getURL(); |
| } |
| return new ArrayIterable<URL>(urls); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.getPath()); |
| } |
| |
| |
| // ********** classpath entry ********** |
| |
| /** |
| * <code>Entry</code> models a Java classpath entry, which can be either a |
| * directory containing <code>.class</code> files or a JAR file (or, |
| * similarly, a <code>.zip</code> file). The entry can return the names of |
| * classes found in it etc. |
| */ |
| public static class Entry |
| implements Serializable |
| { |
| private final String fileName; |
| private final File file; |
| private final File canonicalFile; |
| |
| private static final long serialVersionUID = 1L; |
| |
| Entry(String fileName) { |
| super(); |
| if ((fileName == null) || (fileName.length() == 0)) { |
| throw new IllegalArgumentException("'fileName' must be non-empty"); //$NON-NLS-1$ |
| } |
| this.fileName = fileName; |
| this.file = new File(fileName); |
| this.canonicalFile = FileTools.canonicalFile(this.file); |
| } |
| |
| public String getFileName() { |
| return this.fileName; |
| } |
| |
| public File getFile() { |
| return this.file; |
| } |
| |
| public File getCanonicalFile() { |
| return this.canonicalFile; |
| } |
| |
| public String getCanonicalFileName() { |
| return this.canonicalFile.getAbsolutePath(); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if ( ! (o instanceof Entry)) { |
| return false; |
| } |
| return ((Entry) o).canonicalFile.equals(this.canonicalFile); |
| } |
| |
| @Override |
| public int hashCode() { |
| return this.canonicalFile.hashCode(); |
| } |
| |
| /** |
| * Return the entry's "canonical" URL. |
| */ |
| public URL getURL() { |
| try { |
| return this.canonicalFile.toURI().toURL(); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Return whether the entry contains the specified class. |
| */ |
| public boolean contains(Class<?> javaClass) { |
| return this.contains(javaClass.getName()); |
| } |
| |
| /** |
| * Return whether the entry contains the specified class. |
| */ |
| public boolean contains(String className) { |
| return this.contains(convertToClassFileName(className), convertToArchiveClassFileEntryName(className)); |
| } |
| |
| /** |
| * Return whether the entry contains either the specified relative |
| * class file or the specified archive entry. |
| * Not the prettiest signature, but it's internal.... |
| */ |
| boolean contains(String relativeClassFileName, String archiveEntryName) { |
| if ( ! this.canonicalFile.exists()) { |
| return false; |
| } |
| if (this.canonicalFile.isDirectory() && (new File(this.canonicalFile, relativeClassFileName)).exists()) { |
| return true; |
| } |
| return (fileIsArchive(this.canonicalFile) && this.archiveContainsEntry(archiveEntryName)); |
| } |
| |
| /** |
| * Return whether the entry's archive contains the specified entry. |
| */ |
| private boolean archiveContainsEntry(String zipEntryName) { |
| ZipFile zipFile = null; |
| ZipEntry zipEntry = null; |
| try { |
| zipFile = new ZipFile(this.canonicalFile); |
| zipEntry = zipFile.getEntry(zipEntryName); |
| } catch (IOException ex) { |
| // something is wrong, leave the entry null |
| } finally { |
| try { |
| if (zipFile != null) { |
| zipFile.close(); |
| } |
| } catch (IOException ex) { |
| zipEntry = null; // something is wrong, clear out the entry |
| } |
| } |
| return zipEntry != null; |
| } |
| |
| /** |
| * Return the names of all the classes discovered in the entry. |
| * @see #classNames() |
| */ |
| public Iterable<String> getClassNames() { |
| return this.getClassNames(Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Return the names of all the classes discovered in the entry |
| * and accepted by the specified filter. |
| * @see #classNames(Filter) |
| */ |
| public Iterable<String> getClassNames(Filter<String> filter) { |
| Collection<String> classNames = new ArrayList<String>(2000); |
| this.addClassNamesTo(classNames, filter); |
| return classNames; |
| } |
| |
| /** |
| * Add the names of all the classes discovered in the entry |
| * to the specified collection. |
| */ |
| public void addClassNamesTo(Collection<String> classNames) { |
| this.addClassNamesTo(classNames, Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Add the names of all the classes discovered in the entry |
| * and accepted by the specified filter to the specified collection. |
| */ |
| public void addClassNamesTo(Collection<String> classNames, Filter<String> filter) { |
| if (this.canonicalFile.exists()) { |
| if (this.canonicalFile.isDirectory()) { |
| this.addClassNamesForDirectoryTo(classNames, filter); |
| } else if (fileIsArchive(this.canonicalFile)) { |
| this.addClassNamesForArchiveTo(classNames, filter); |
| } |
| } |
| } |
| |
| /** |
| * Add the names of all the classes discovered |
| * under the entry's directory and accepted by |
| * the specified filter to the specified collection. |
| */ |
| private void addClassNamesForDirectoryTo(Collection<String> classNames, Filter<String> filter) { |
| int start = this.canonicalFile.getAbsolutePath().length() + 1; |
| for (Iterator<File> stream = this.classFilesForDirectory(); stream.hasNext(); ) { |
| String className = convertToClassName(stream.next().getAbsolutePath().substring(start)); |
| if (filter.accept(className)) { |
| classNames.add(className); |
| } |
| } |
| } |
| |
| /** |
| * Return an iterator on all the class files discovered |
| * under the entry's directory. |
| */ |
| private Iterator<File> classFilesForDirectory() { |
| return new FilteringIterator<File>(FileTools.filesInTree(this.canonicalFile)) { |
| @Override |
| protected boolean accept(File next) { |
| return Entry.this.fileNameMightBeForClassFile(next.getName()); |
| } |
| }; |
| } |
| |
| /** |
| * Add the names of all the classes discovered |
| * in the entry's archive file and accepted by the |
| * specified filter to the specified collection. |
| */ |
| private void addClassNamesForArchiveTo(Collection<String> classNames, Filter<String> filter) { |
| ZipFile zipFile = null; |
| try { |
| zipFile = new ZipFile(this.canonicalFile); |
| } catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| for (Enumeration<? extends ZipEntry> stream = zipFile.entries(); stream.hasMoreElements(); ) { |
| ZipEntry zipEntry = stream.nextElement(); |
| String zipEntryName = zipEntry.getName(); |
| if (this.fileNameMightBeForClassFile(zipEntryName)) { |
| String className = convertToClassName(zipEntryName); |
| if (filter.accept(className)) { |
| classNames.add(className); |
| } |
| } |
| } |
| try { |
| zipFile.close(); |
| } catch (IOException ex) { |
| return; |
| } |
| } |
| |
| /** |
| * Return whether the specified file might be a Java class file. |
| * The file name must at least end with <code>".class"</code> and contain no spaces. |
| * (Neither class names nor package names may contain spaces.) |
| * Whether it actually is a class file will need to be determined by |
| * a class loader. |
| */ |
| boolean fileNameMightBeForClassFile(String name) { |
| return FileTools.extension(name).toLowerCase().equals(".class") //$NON-NLS-1$ |
| && (name.indexOf(' ') == -1); |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath. |
| * Just a bit more performant than {@link #getClassNames()}. |
| */ |
| public Iterator<String> classNames() { |
| return this.classNames(Filter.Transparent.<String>instance()); |
| } |
| |
| /** |
| * Return the names of all the classes discovered on the classpath |
| * that are accepted by the specified filter. |
| * Just a bit more performant than {@link #getClassNames(Filter)}. |
| */ |
| public Iterator<String> classNames(Filter<String> filter) { |
| if (this.canonicalFile.exists()) { |
| if (this.canonicalFile.isDirectory()) { |
| return this.classNamesForDirectory(filter); |
| } |
| if (fileIsArchive(this.canonicalFile)) { |
| return this.classNamesForArchive(filter); |
| } |
| } |
| return EmptyIterator.instance(); |
| } |
| |
| /** |
| * Return the names of all the classes discovered |
| * under the entry's directory and accepted by |
| * the specified filter. |
| */ |
| private Iterator<String> classNamesForDirectory(Filter<String> filter) { |
| return new FilteringIterator<String>(this.classNamesForDirectory(), filter); |
| } |
| |
| /** |
| * Transform the class files to class names. |
| */ |
| private Iterator<String> classNamesForDirectory() { |
| final int start = this.canonicalFile.getAbsolutePath().length() + 1; |
| return new TransformationIterator<File, String>(this.classFilesForDirectory()) { |
| @Override |
| protected String transform(File f) { |
| return convertToClassName(f.getAbsolutePath().substring(start)); |
| } |
| }; |
| } |
| |
| /** |
| * Return the names of all the classes discovered |
| * in the entry's archive file and accepted by the |
| * specified filter. |
| */ |
| private Iterator<String> classNamesForArchive(Filter<String> filter) { |
| // we can't simply wrap iterators here because we need to close the archive file... |
| ZipFile zipFile = null; |
| try { |
| zipFile = new ZipFile(this.canonicalFile); |
| } catch (IOException ex) { |
| return EmptyIterator.instance(); |
| } |
| Collection<String> classNames = new HashSet<String>(zipFile.size()); |
| for (Enumeration<? extends ZipEntry> stream = zipFile.entries(); stream.hasMoreElements(); ) { |
| ZipEntry zipEntry = stream.nextElement(); |
| String zipEntryName = zipEntry.getName(); |
| if (this.fileNameMightBeForClassFile(zipEntryName)) { |
| String className = convertToClassName(zipEntryName); |
| if (filter.accept(className)) { |
| classNames.add(className); |
| } |
| } |
| } |
| try { |
| zipFile.close(); |
| } catch (IOException ex) { |
| return EmptyIterator.instance(); |
| } |
| return classNames.iterator(); |
| } |
| } |
| } |