17dd252788645e940eada959bdde927426e2531c9Paul Duffin/*
27dd252788645e940eada959bdde927426e2531c9Paul Duffin * Copyright (C) 2012 The Guava Authors
37dd252788645e940eada959bdde927426e2531c9Paul Duffin *
47dd252788645e940eada959bdde927426e2531c9Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License");
57dd252788645e940eada959bdde927426e2531c9Paul Duffin * you may not use this file except in compliance with the License.
67dd252788645e940eada959bdde927426e2531c9Paul Duffin * You may obtain a copy of the License at
77dd252788645e940eada959bdde927426e2531c9Paul Duffin *
87dd252788645e940eada959bdde927426e2531c9Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0
97dd252788645e940eada959bdde927426e2531c9Paul Duffin *
107dd252788645e940eada959bdde927426e2531c9Paul Duffin * Unless required by applicable law or agreed to in writing, software
117dd252788645e940eada959bdde927426e2531c9Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS,
127dd252788645e940eada959bdde927426e2531c9Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137dd252788645e940eada959bdde927426e2531c9Paul Duffin * See the License for the specific language governing permissions and
147dd252788645e940eada959bdde927426e2531c9Paul Duffin * limitations under the License.
157dd252788645e940eada959bdde927426e2531c9Paul Duffin */
167dd252788645e940eada959bdde927426e2531c9Paul Duffin
177dd252788645e940eada959bdde927426e2531c9Paul Duffinpackage com.google.common.reflect;
187dd252788645e940eada959bdde927426e2531c9Paul Duffin
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull;
207dd252788645e940eada959bdde927426e2531c9Paul Duffin
217dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.Beta;
227dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.VisibleForTesting;
230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.base.CharMatcher;
240888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.base.Predicate;
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.base.Splitter;
260888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.collect.FluentIterable;
277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableMap;
287dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableSet;
297dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableSortedSet;
307dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Maps;
317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Ordering;
320888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.collect.Sets;
337dd252788645e940eada959bdde927426e2531c9Paul Duffin
347dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.io.File;
357dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.io.IOException;
367dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.net.URI;
377dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.net.URISyntaxException;
387dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.net.URL;
397dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.net.URLClassLoader;
407dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Enumeration;
417dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.LinkedHashMap;
427dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Map;
430888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.Set;
440888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.jar.Attributes;
457dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.jar.JarEntry;
467dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.jar.JarFile;
477dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.jar.Manifest;
487dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.logging.Logger;
497dd252788645e940eada959bdde927426e2531c9Paul Duffin
507dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.Nullable;
517dd252788645e940eada959bdde927426e2531c9Paul Duffin
527dd252788645e940eada959bdde927426e2531c9Paul Duffin/**
530888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Scans the source of a {@link ClassLoader} and finds all loadable classes and resources.
547dd252788645e940eada959bdde927426e2531c9Paul Duffin *
557dd252788645e940eada959bdde927426e2531c9Paul Duffin * @author Ben Yu
567dd252788645e940eada959bdde927426e2531c9Paul Duffin * @since 14.0
577dd252788645e940eada959bdde927426e2531c9Paul Duffin */
587dd252788645e940eada959bdde927426e2531c9Paul Duffin@Beta
597dd252788645e940eada959bdde927426e2531c9Paul Duffinpublic final class ClassPath {
607dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final Logger logger = Logger.getLogger(ClassPath.class.getName());
617dd252788645e940eada959bdde927426e2531c9Paul Duffin
620888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Predicate<ClassInfo> IS_TOP_LEVEL = new Predicate<ClassInfo>() {
630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public boolean apply(ClassInfo info) {
640888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return info.className.indexOf('$') == -1;
650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
660888a09821a98ac0680fad765217302858e70fa4Paul Duffin  };
670888a09821a98ac0680fad765217302858e70fa4Paul Duffin
687dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Separator for the Class-Path manifest attribute value in jar files. */
690888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR =
700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      Splitter.on(" ").omitEmptyStrings();
717dd252788645e940eada959bdde927426e2531c9Paul Duffin
727dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final String CLASS_FILE_NAME_EXTENSION = ".class";
737dd252788645e940eada959bdde927426e2531c9Paul Duffin
747dd252788645e940eada959bdde927426e2531c9Paul Duffin  private final ImmutableSet<ResourceInfo> resources;
757dd252788645e940eada959bdde927426e2531c9Paul Duffin
767dd252788645e940eada959bdde927426e2531c9Paul Duffin  private ClassPath(ImmutableSet<ResourceInfo> resources) {
777dd252788645e940eada959bdde927426e2531c9Paul Duffin    this.resources = resources;
787dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
797dd252788645e940eada959bdde927426e2531c9Paul Duffin
807dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
817dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns a {@code ClassPath} representing all classes and resources loadable from {@code
827dd252788645e940eada959bdde927426e2531c9Paul Duffin   * classloader} and its parent class loaders.
837dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
847dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p>Currently only {@link URLClassLoader} and only {@code file://} urls are supported.
857dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
867dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IOException if the attempt to read class path resources (jar files or directories)
877dd252788645e940eada959bdde927426e2531c9Paul Duffin   *         failed.
887dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
897dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static ClassPath from(ClassLoader classloader) throws IOException {
900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Scanner scanner = new Scanner();
917dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (Map.Entry<URI, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
920888a09821a98ac0680fad765217302858e70fa4Paul Duffin      scanner.scan(entry.getKey(), entry.getValue());
937dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return new ClassPath(scanner.getResources());
957dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
967dd252788645e940eada959bdde927426e2531c9Paul Duffin
977dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
987dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns all resources loadable from the current class path, including the class files of all
990888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * loadable classes but excluding the "META-INF/MANIFEST.MF" file.
1007dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1017dd252788645e940eada959bdde927426e2531c9Paul Duffin  public ImmutableSet<ResourceInfo> getResources() {
1027dd252788645e940eada959bdde927426e2531c9Paul Duffin    return resources;
1037dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1047dd252788645e940eada959bdde927426e2531c9Paul Duffin
1050888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
1060888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * Returns all classes loadable from the current class path.
1070888a09821a98ac0680fad765217302858e70fa4Paul Duffin   *
1080888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * @since 16.0
1090888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
1100888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public ImmutableSet<ClassInfo> getAllClasses() {
1110888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return FluentIterable.from(resources).filter(ClassInfo.class).toSet();
1120888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
1130888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1147dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Returns all top level classes loadable from the current class path. */
1157dd252788645e940eada959bdde927426e2531c9Paul Duffin  public ImmutableSet<ClassInfo> getTopLevelClasses() {
1160888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return FluentIterable.from(resources).filter(ClassInfo.class).filter(IS_TOP_LEVEL).toSet();
1177dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1187dd252788645e940eada959bdde927426e2531c9Paul Duffin
1197dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Returns all top level classes whose package name is {@code packageName}. */
1207dd252788645e940eada959bdde927426e2531c9Paul Duffin  public ImmutableSet<ClassInfo> getTopLevelClasses(String packageName) {
1217dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkNotNull(packageName);
1227dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder();
1237dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (ClassInfo classInfo : getTopLevelClasses()) {
1247dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (classInfo.getPackageName().equals(packageName)) {
1257dd252788645e940eada959bdde927426e2531c9Paul Duffin        builder.add(classInfo);
1267dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1277dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1287dd252788645e940eada959bdde927426e2531c9Paul Duffin    return builder.build();
1297dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1307dd252788645e940eada959bdde927426e2531c9Paul Duffin
1317dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Returns all top level classes whose package name is {@code packageName} or starts with
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code packageName} followed by a '.'.
1347dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1357dd252788645e940eada959bdde927426e2531c9Paul Duffin  public ImmutableSet<ClassInfo> getTopLevelClassesRecursive(String packageName) {
1367dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkNotNull(packageName);
1377dd252788645e940eada959bdde927426e2531c9Paul Duffin    String packagePrefix = packageName + '.';
1387dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder();
1397dd252788645e940eada959bdde927426e2531c9Paul Duffin    for (ClassInfo classInfo : getTopLevelClasses()) {
1407dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (classInfo.getName().startsWith(packagePrefix)) {
1417dd252788645e940eada959bdde927426e2531c9Paul Duffin        builder.add(classInfo);
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1437dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1447dd252788645e940eada959bdde927426e2531c9Paul Duffin    return builder.build();
1457dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1467dd252788645e940eada959bdde927426e2531c9Paul Duffin
1477dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1487dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Represents a class path resource that can be either a class file or any other resource file
1497dd252788645e940eada959bdde927426e2531c9Paul Duffin   * loadable from the class path.
1507dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1517dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @since 14.0
1527dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1537dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Beta
1547dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static class ResourceInfo {
1557dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final String resourceName;
1567dd252788645e940eada959bdde927426e2531c9Paul Duffin    final ClassLoader loader;
1577dd252788645e940eada959bdde927426e2531c9Paul Duffin
1587dd252788645e940eada959bdde927426e2531c9Paul Duffin    static ResourceInfo of(String resourceName, ClassLoader loader) {
1590888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION)) {
1607dd252788645e940eada959bdde927426e2531c9Paul Duffin        return new ClassInfo(resourceName, loader);
1617dd252788645e940eada959bdde927426e2531c9Paul Duffin      } else {
1627dd252788645e940eada959bdde927426e2531c9Paul Duffin        return new ResourceInfo(resourceName, loader);
1637dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1647dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1650888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1667dd252788645e940eada959bdde927426e2531c9Paul Duffin    ResourceInfo(String resourceName, ClassLoader loader) {
1677dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.resourceName = checkNotNull(resourceName);
1687dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.loader = checkNotNull(loader);
1697dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1707dd252788645e940eada959bdde927426e2531c9Paul Duffin
1717dd252788645e940eada959bdde927426e2531c9Paul Duffin    /** Returns the url identifying the resource. */
1727dd252788645e940eada959bdde927426e2531c9Paul Duffin    public final URL url() {
1737dd252788645e940eada959bdde927426e2531c9Paul Duffin      return checkNotNull(loader.getResource(resourceName),
1747dd252788645e940eada959bdde927426e2531c9Paul Duffin          "Failed to load resource: %s", resourceName);
1757dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1767dd252788645e940eada959bdde927426e2531c9Paul Duffin
1777dd252788645e940eada959bdde927426e2531c9Paul Duffin    /** Returns the fully qualified name of the resource. Such as "com/mycomp/foo/bar.txt". */
1787dd252788645e940eada959bdde927426e2531c9Paul Duffin    public final String getResourceName() {
1797dd252788645e940eada959bdde927426e2531c9Paul Duffin      return resourceName;
1807dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1817dd252788645e940eada959bdde927426e2531c9Paul Duffin
1827dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public int hashCode() {
1837dd252788645e940eada959bdde927426e2531c9Paul Duffin      return resourceName.hashCode();
1847dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1857dd252788645e940eada959bdde927426e2531c9Paul Duffin
1867dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public boolean equals(Object obj) {
1877dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (obj instanceof ResourceInfo) {
1887dd252788645e940eada959bdde927426e2531c9Paul Duffin        ResourceInfo that = (ResourceInfo) obj;
1897dd252788645e940eada959bdde927426e2531c9Paul Duffin        return resourceName.equals(that.resourceName)
1907dd252788645e940eada959bdde927426e2531c9Paul Duffin            && loader == that.loader;
1917dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
1927dd252788645e940eada959bdde927426e2531c9Paul Duffin      return false;
1937dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1947dd252788645e940eada959bdde927426e2531c9Paul Duffin
1950888a09821a98ac0680fad765217302858e70fa4Paul Duffin    // Do not change this arbitrarily. We rely on it for sorting ResourceInfo.
1967dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public String toString() {
1977dd252788645e940eada959bdde927426e2531c9Paul Duffin      return resourceName;
1987dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
1997dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2007dd252788645e940eada959bdde927426e2531c9Paul Duffin
2017dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2027dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Represents a class that can be loaded through {@link #load}.
2037dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
2047dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @since 14.0
2057dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2067dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Beta
2077dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static final class ClassInfo extends ResourceInfo {
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin    private final String className;
2097dd252788645e940eada959bdde927426e2531c9Paul Duffin
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin    ClassInfo(String resourceName, ClassLoader loader) {
2117dd252788645e940eada959bdde927426e2531c9Paul Duffin      super(resourceName, loader);
2127dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.className = getClassName(resourceName);
2137dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2147dd252788645e940eada959bdde927426e2531c9Paul Duffin
2150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
2160888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Returns the package name of the class, without attempting to load the class.
2170888a09821a98ac0680fad765217302858e70fa4Paul Duffin     *
2180888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * <p>Behaves identically to {@link Package#getName()} but does not require the class (or
2190888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * package) to be loaded.
2200888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin    public String getPackageName() {
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin      return Reflection.getPackageName(className);
2237dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2247dd252788645e940eada959bdde927426e2531c9Paul Duffin
2250888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
2260888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Returns the simple name of the underlying class as given in the source code.
2270888a09821a98ac0680fad765217302858e70fa4Paul Duffin     *
2280888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * <p>Behaves identically to {@link Class#getSimpleName()} but does not require the class to be
2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * loaded.
2300888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
2317dd252788645e940eada959bdde927426e2531c9Paul Duffin    public String getSimpleName() {
2320888a09821a98ac0680fad765217302858e70fa4Paul Duffin      int lastDollarSign = className.lastIndexOf('$');
2330888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (lastDollarSign != -1) {
2340888a09821a98ac0680fad765217302858e70fa4Paul Duffin        String innerClassName = className.substring(lastDollarSign + 1);
2350888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // local and anonymous classes are prefixed with number (1,2,3...), anonymous classes are
2360888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // entirely numeric whereas local classes have the user supplied name as a suffix
2370888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return CharMatcher.DIGIT.trimLeadingFrom(innerClassName);
2380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2397dd252788645e940eada959bdde927426e2531c9Paul Duffin      String packageName = getPackageName();
2403ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      if (packageName.isEmpty()) {
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin        return className;
2427dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2447dd252788645e940eada959bdde927426e2531c9Paul Duffin      // Since this is a top level class, its simple name is always the part after package name.
2457dd252788645e940eada959bdde927426e2531c9Paul Duffin      return className.substring(packageName.length() + 1);
2467dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2477dd252788645e940eada959bdde927426e2531c9Paul Duffin
2480888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
2490888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Returns the fully qualified name of the class.
2500888a09821a98ac0680fad765217302858e70fa4Paul Duffin     *
2510888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * <p>Behaves identically to {@link Class#getName()} but does not require the class to be
2520888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * loaded.
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
2547dd252788645e940eada959bdde927426e2531c9Paul Duffin    public String getName() {
2557dd252788645e940eada959bdde927426e2531c9Paul Duffin      return className;
2567dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2577dd252788645e940eada959bdde927426e2531c9Paul Duffin
2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
2590888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Loads (but doesn't link or initialize) the class.
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin     *
2610888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * @throws LinkageError when there were errors in loading classes that this class depends on.
2620888a09821a98ac0680fad765217302858e70fa4Paul Duffin     *         For example, {@link NoClassDefFoundError}.
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
2647dd252788645e940eada959bdde927426e2531c9Paul Duffin    public Class<?> load() {
2657dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
2667dd252788645e940eada959bdde927426e2531c9Paul Duffin        return loader.loadClass(className);
2677dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (ClassNotFoundException e) {
2687dd252788645e940eada959bdde927426e2531c9Paul Duffin        // Shouldn't happen, since the class name is read from the class path.
2697dd252788645e940eada959bdde927426e2531c9Paul Duffin        throw new IllegalStateException(e);
2707dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public String toString() {
2747dd252788645e940eada959bdde927426e2531c9Paul Duffin      return className;
2757dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2767dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2777dd252788645e940eada959bdde927426e2531c9Paul Duffin
2780888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting static ImmutableMap<URI, ClassLoader> getClassPathEntries(
2790888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ClassLoader classloader) {
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin    LinkedHashMap<URI, ClassLoader> entries = Maps.newLinkedHashMap();
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin    // Search parent first, since it's the order ClassLoader#loadClass() uses.
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin    ClassLoader parent = classloader.getParent();
2837dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (parent != null) {
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin      entries.putAll(getClassPathEntries(parent));
2857dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2867dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (classloader instanceof URLClassLoader) {
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin      URLClassLoader urlClassLoader = (URLClassLoader) classloader;
2887dd252788645e940eada959bdde927426e2531c9Paul Duffin      for (URL entry : urlClassLoader.getURLs()) {
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin        URI uri;
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin        try {
2917dd252788645e940eada959bdde927426e2531c9Paul Duffin          uri = entry.toURI();
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin        } catch (URISyntaxException e) {
2937dd252788645e940eada959bdde927426e2531c9Paul Duffin          throw new IllegalArgumentException(e);
2947dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin        if (!entries.containsKey(uri)) {
2967dd252788645e940eada959bdde927426e2531c9Paul Duffin          entries.put(uri, classloader);
2977dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
2987dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2997dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3007dd252788645e940eada959bdde927426e2531c9Paul Duffin    return ImmutableMap.copyOf(entries);
3017dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3027dd252788645e940eada959bdde927426e2531c9Paul Duffin
3030888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting static final class Scanner {
3047dd252788645e940eada959bdde927426e2531c9Paul Duffin
3050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private final ImmutableSortedSet.Builder<ResourceInfo> resources =
3060888a09821a98ac0680fad765217302858e70fa4Paul Duffin        new ImmutableSortedSet.Builder<ResourceInfo>(Ordering.usingToString());
3070888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private final Set<URI> scannedUris = Sets.newHashSet();
3087dd252788645e940eada959bdde927426e2531c9Paul Duffin
3090888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ImmutableSortedSet<ResourceInfo> getResources() {
3100888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return resources.build();
3110888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
3127dd252788645e940eada959bdde927426e2531c9Paul Duffin
3130888a09821a98ac0680fad765217302858e70fa4Paul Duffin    void scan(URI uri, ClassLoader classloader) throws IOException {
3140888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (uri.getScheme().equals("file") && scannedUris.add(uri)) {
3150888a09821a98ac0680fad765217302858e70fa4Paul Duffin        scanFrom(new File(uri), classloader);
3160888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3170888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
3180888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @VisibleForTesting void scanFrom(File file, ClassLoader classloader)
3200888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throws IOException {
3210888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (!file.exists()) {
3220888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return;
3230888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3240888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (file.isDirectory()) {
3250888a09821a98ac0680fad765217302858e70fa4Paul Duffin        scanDirectory(file, classloader);
3267dd252788645e940eada959bdde927426e2531c9Paul Duffin      } else {
3270888a09821a98ac0680fad765217302858e70fa4Paul Duffin        scanJar(file, classloader);
3287dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3297dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3300888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private void scanDirectory(File directory, ClassLoader classloader) throws IOException {
3320888a09821a98ac0680fad765217302858e70fa4Paul Duffin      scanDirectory(directory, classloader, "", ImmutableSet.<File>of());
3337dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3340888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3350888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private void scanDirectory(
3360888a09821a98ac0680fad765217302858e70fa4Paul Duffin        File directory, ClassLoader classloader, String packagePrefix,
3370888a09821a98ac0680fad765217302858e70fa4Paul Duffin        ImmutableSet<File> ancestors) throws IOException {
3380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      File canonical = directory.getCanonicalFile();
3390888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (ancestors.contains(canonical)) {
3400888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // A cycle in the filesystem, for example due to a symbolic link.
3410888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return;
3420888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3430888a09821a98ac0680fad765217302858e70fa4Paul Duffin      File[] files = directory.listFiles();
3440888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (files == null) {
3450888a09821a98ac0680fad765217302858e70fa4Paul Duffin        logger.warning("Cannot read directory " + directory);
3460888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // IO error, just skip the directory
3470888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return;
3487dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3490888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ImmutableSet<File> newAncestors = ImmutableSet.<File>builder()
3500888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .addAll(ancestors)
3510888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .add(canonical)
3520888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .build();
3530888a09821a98ac0680fad765217302858e70fa4Paul Duffin      for (File f : files) {
3540888a09821a98ac0680fad765217302858e70fa4Paul Duffin        String name = f.getName();
3550888a09821a98ac0680fad765217302858e70fa4Paul Duffin        if (f.isDirectory()) {
3560888a09821a98ac0680fad765217302858e70fa4Paul Duffin          scanDirectory(f, classloader, packagePrefix + name + "/", newAncestors);
3570888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } else {
3580888a09821a98ac0680fad765217302858e70fa4Paul Duffin          String resourceName = packagePrefix + name;
3590888a09821a98ac0680fad765217302858e70fa4Paul Duffin          if (!resourceName.equals(JarFile.MANIFEST_NAME)) {
3600888a09821a98ac0680fad765217302858e70fa4Paul Duffin            resources.add(ResourceInfo.of(resourceName, classloader));
3610888a09821a98ac0680fad765217302858e70fa4Paul Duffin          }
3627dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
3637dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
3647dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
3650888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3660888a09821a98ac0680fad765217302858e70fa4Paul Duffin    private void scanJar(File file, ClassLoader classloader) throws IOException {
3670888a09821a98ac0680fad765217302858e70fa4Paul Duffin      JarFile jarFile;
3680888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
3690888a09821a98ac0680fad765217302858e70fa4Paul Duffin        jarFile = new JarFile(file);
3700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IOException e) {
3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // Not a jar file
3720888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return;
3730888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3740888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
3750888a09821a98ac0680fad765217302858e70fa4Paul Duffin        for (URI uri : getClassPathFromManifest(file, jarFile.getManifest())) {
3760888a09821a98ac0680fad765217302858e70fa4Paul Duffin          scan(uri, classloader);
3770888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin        Enumeration<JarEntry> entries = jarFile.entries();
3790888a09821a98ac0680fad765217302858e70fa4Paul Duffin        while (entries.hasMoreElements()) {
3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin          JarEntry entry = entries.nextElement();
3810888a09821a98ac0680fad765217302858e70fa4Paul Duffin          if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) {
3820888a09821a98ac0680fad765217302858e70fa4Paul Duffin            continue;
3830888a09821a98ac0680fad765217302858e70fa4Paul Duffin          }
3840888a09821a98ac0680fad765217302858e70fa4Paul Duffin          resources.add(ResourceInfo.of(entry.getName(), classloader));
3850888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
3860888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } finally {
3877dd252788645e940eada959bdde927426e2531c9Paul Duffin        try {
3880888a09821a98ac0680fad765217302858e70fa4Paul Duffin          jarFile.close();
3890888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IOException ignored) {}
3900888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
3920888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3930888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
3940888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
3950888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * to <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
3960888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * JAR File Specification</a>. If {@code manifest} is null, it means the jar file has no
3970888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * manifest, and an empty set will be returned.
3980888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
3990888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @VisibleForTesting static ImmutableSet<URI> getClassPathFromManifest(
4000888a09821a98ac0680fad765217302858e70fa4Paul Duffin        File jarFile, @Nullable Manifest manifest) {
4010888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (manifest == null) {
4020888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return ImmutableSet.of();
4030888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4040888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ImmutableSet.Builder<URI> builder = ImmutableSet.builder();
4050888a09821a98ac0680fad765217302858e70fa4Paul Duffin      String classpathAttribute = manifest.getMainAttributes()
4060888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .getValue(Attributes.Name.CLASS_PATH.toString());
4070888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (classpathAttribute != null) {
4080888a09821a98ac0680fad765217302858e70fa4Paul Duffin        for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
4090888a09821a98ac0680fad765217302858e70fa4Paul Duffin          URI uri;
4100888a09821a98ac0680fad765217302858e70fa4Paul Duffin          try {
4110888a09821a98ac0680fad765217302858e70fa4Paul Duffin            uri = getClassPathEntry(jarFile, path);
4120888a09821a98ac0680fad765217302858e70fa4Paul Duffin          } catch (URISyntaxException e) {
4130888a09821a98ac0680fad765217302858e70fa4Paul Duffin            // Ignore bad entry
4140888a09821a98ac0680fad765217302858e70fa4Paul Duffin            logger.warning("Invalid Class-Path entry: " + path);
4150888a09821a98ac0680fad765217302858e70fa4Paul Duffin            continue;
4160888a09821a98ac0680fad765217302858e70fa4Paul Duffin          }
4170888a09821a98ac0680fad765217302858e70fa4Paul Duffin          builder.add(uri);
4187dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
4197dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
4200888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return builder.build();
4217dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4220888a09821a98ac0680fad765217302858e70fa4Paul Duffin
4230888a09821a98ac0680fad765217302858e70fa4Paul Duffin    /**
4240888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * Returns the absolute uri of the Class-Path entry value as specified in
4250888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
4260888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * JAR File Specification</a>. Even though the specification only talks about relative urls,
4270888a09821a98ac0680fad765217302858e70fa4Paul Duffin     * absolute urls are actually supported too (for example, in Maven surefire plugin).
4280888a09821a98ac0680fad765217302858e70fa4Paul Duffin     */
4290888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @VisibleForTesting static URI getClassPathEntry(File jarFile, String path)
4300888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throws URISyntaxException {
4310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      URI uri = new URI(path);
4320888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (uri.isAbsolute()) {
4330888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return uri;
4340888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } else {
4350888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI();
4360888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
4377dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4387dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4397dd252788645e940eada959bdde927426e2531c9Paul Duffin
4407dd252788645e940eada959bdde927426e2531c9Paul Duffin  @VisibleForTesting static String getClassName(String filename) {
4417dd252788645e940eada959bdde927426e2531c9Paul Duffin    int classNameEnd = filename.length() - CLASS_FILE_NAME_EXTENSION.length();
4427dd252788645e940eada959bdde927426e2531c9Paul Duffin    return filename.substring(0, classNameEnd).replace('/', '.');
4437dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4447dd252788645e940eada959bdde927426e2531c9Paul Duffin}
445