FileDocCategorySizeDatePackage
ClassPathPackageInfoSource.javaAPI DocAndroid 1.5 API13220Wed May 06 22:42:02 BST 2009android.test

ClassPathPackageInfoSource

public class ClassPathPackageInfoSource extends Object
Generate {@link ClassPathPackageInfo}s by scanning apk paths. {@hide} Not needed for 1.0 SDK.

Fields Summary
private static final String
CLASS_EXTENSION
private static final ClassLoader
CLASS_LOADER
private final SimpleCache
cache
private final String[]
classPath
private static String[]
apkPaths
private final Map
jarFiles
private ClassLoader
classLoader
Constructors Summary
ClassPathPackageInfoSource()


     
        classPath = getClassPath();
    
Methods Summary
private ClassPathPackageInfocreatePackageInfo(java.lang.String packageName)

        Set<String> subpackageNames = new TreeSet<String>();
        Set<String> classNames = new TreeSet<String>();
        Set<Class<?>> topLevelClasses = Sets.newHashSet();
        findClasses(packageName, classNames, subpackageNames);
        for (String className : classNames) {
            if (className.endsWith(".R") || className.endsWith(".Manifest")) {
                // Don't try to load classes that are generated. They usually aren't in test apks.
                continue;
            }
            
            try {
                // We get errors in the emulator if we don't use the caller's class loader.
                topLevelClasses.add(Class.forName(className, false,
                        (classLoader != null) ? classLoader : CLASS_LOADER));
            } catch (ClassNotFoundException e) {
                // Should not happen unless there is a generated class that is not included in
                // the .apk.
                Log.w("ClassPathPackageInfoSource", "Cannot load class. "
                        + "Make sure it is in your apk. Class name: '" + className
                        + "'. Message: " + e.getMessage(), e);
            }
        }
        return new ClassPathPackageInfo(this, packageName, subpackageNames,
                topLevelClasses);
    
private voidfindClasses(java.lang.String packageName, java.util.Set classNames, java.util.Set subpackageNames)
Finds all classes and sub packages that are below the packageName and add them to the respective sets. Searches the package on the whole class path.

        String packagePrefix = packageName + '.";
        String pathPrefix = packagePrefix.replace('.", '/");

        for (String entryName : classPath) {
            File classPathEntry = new File(entryName);

            // Forge may not have brought over every item in the classpath. Be
            // polite and ignore missing entries.
            if (classPathEntry.exists()) {
                try {
                    if (entryName.endsWith(".apk")) {
                        findClassesInApk(entryName, packageName, classNames, subpackageNames);
                    } else if ("true".equals(System.getProperty("android.vm.dexfile", "false"))) {
                        // If the vm supports dex files then scan the directories that contain
                        // apk files. 
                        for (String apkPath : apkPaths) {
                            File file = new File(apkPath);
                            scanForApkFiles(file, packageName, classNames, subpackageNames);
                        }
                    } else if (entryName.endsWith(".jar")) {
                        findClassesInJar(classPathEntry, pathPrefix,
                                classNames, subpackageNames);
                    } else if (classPathEntry.isDirectory()) {
                        findClassesInDirectory(classPathEntry, packagePrefix, pathPrefix,
                                classNames, subpackageNames);
                    } else {
                        throw new AssertionError("Don't understand classpath entry " +
                                classPathEntry);
                    }
                } catch (IOException e) {
                    throw new AssertionError("Can't read classpath entry " +
                            entryName + ": " + e.getMessage());
                }
            }
        }
    
private voidfindClassesInApk(java.lang.String apkPath, java.lang.String packageName, java.util.Set classNames, java.util.Set subpackageNames)
Finds all classes and sub packages that are below the packageName and add them to the respective sets. Searches the package in a single apk file.


        DexFile dexFile = null;
        try {
            dexFile = new DexFile(apkPath);
            Enumeration<String> apkClassNames = dexFile.entries();
            while (apkClassNames.hasMoreElements()) {
                String className = apkClassNames.nextElement();

                if (className.startsWith(packageName)) {
                    String subPackageName = packageName;
                    int lastPackageSeparator = className.lastIndexOf('.");
                    if (lastPackageSeparator > 0) {
                        subPackageName = className.substring(0, lastPackageSeparator);
                    }
                    if (subPackageName.length() > packageName.length()) {
                        subpackageNames.add(subPackageName);
                    } else if (isToplevelClass(className)) {
                        classNames.add(className);
                    }
                }
            }
        } catch (IOException e) {
            if (Config.LOGV) {
                Log.w("ClassPathPackageInfoSource",
                        "Error finding classes at apk path: " + apkPath, e);
            }
        } finally {
            if (dexFile != null) {
                // Todo: figure out why closing causes a dalvik error resulting in vm shutdown.
//                dexFile.close();
            }
        }
    
private voidfindClassesInDirectory(java.io.File classDir, java.lang.String packagePrefix, java.lang.String pathPrefix, java.util.Set classNames, java.util.Set subpackageNames)
Finds all classes and sub packages that are below the packageName and add them to the respective sets. Searches the package in a class directory.

        File directory = new File(classDir, pathPrefix);

        if (directory.exists()) {
            for (File f : directory.listFiles()) {
                String name = f.getName();
                if (name.endsWith(CLASS_EXTENSION) && isToplevelClass(name)) {
                    classNames.add(packagePrefix + getClassName(name));
                } else if (f.isDirectory()) {
                    subpackageNames.add(packagePrefix + name);
                }
            }
        }
    
private voidfindClassesInJar(java.io.File jarFile, java.lang.String pathPrefix, java.util.Set classNames, java.util.Set subpackageNames)
Finds all classes and sub packages that are below the packageName and add them to the respective sets. Searches the package in a single jar file.

        Set<String> entryNames = getJarEntries(jarFile);
        // check if the Jar contains the package.
        if (!entryNames.contains(pathPrefix)) {
            return;
        }
        int prefixLength = pathPrefix.length();
        for (String entryName : entryNames) {
            if (entryName.startsWith(pathPrefix)) {
                if (entryName.endsWith(CLASS_EXTENSION)) {
                    // check if the class is in the package itself or in one of its
                    // subpackages.
                    int index = entryName.indexOf('/", prefixLength);
                    if (index >= 0) {
                        String p = entryName.substring(0, index).replace('/", '.");
                        subpackageNames.add(p);
                    } else if (isToplevelClass(entryName)) {
                        classNames.add(getClassName(entryName).replace('/", '."));
                    }
                }
            }
        }
    
private static java.lang.StringgetClassName(java.lang.String className)
Given the absolute path of a class file, return the class name.

        int classNameEnd = className.length() - CLASS_EXTENSION.length();
        return className.substring(0, classNameEnd);
    
private static java.lang.String[]getClassPath()
Gets the class path from the System Property "java.class.path" and splits it up into the individual elements.

        String classPath = System.getProperty("java.class.path");
        String separator = System.getProperty("path.separator", ":");
        return classPath.split(Pattern.quote(separator));
    
private java.util.SetgetJarEntries(java.io.File jarFile)
Gets the class and package entries from a Jar.

        Set<String> entryNames = jarFiles.get(jarFile);
        if (entryNames == null) {
            entryNames = Sets.newHashSet();
            ZipFile zipFile = new ZipFile(jarFile);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                String entryName = entries.nextElement().getName();
                if (entryName.endsWith(CLASS_EXTENSION)) {
                    // add the entry name of the class
                    entryNames.add(entryName);

                    // add the entry name of the classes package, i.e. the entry name of
                    // the directory that the class is in. Used to quickly skip jar files
                    // if they do not contain a certain package.
                    //
                    // Also add parent packages so that a JAR that contains
                    // pkg1/pkg2/Foo.class will be marked as containing pkg1/ in addition
                    // to pkg1/pkg2/ and pkg1/pkg2/Foo.class.  We're still interested in
                    // JAR files that contains subpackages of a given package, even if
                    // an intermediate package contains no direct classes.
                    //
                    // Classes in the default package will cause a single package named
                    // "" to be added instead.
                    int lastIndex = entryName.lastIndexOf('/");
                    do {
                        String packageName = entryName.substring(0, lastIndex + 1);
                        entryNames.add(packageName);
                        lastIndex = entryName.lastIndexOf('/", lastIndex - 1);
                    } while (lastIndex > 0);
                }
            }
            jarFiles.put(jarFile, entryNames);
        }
        return entryNames;
    
public ClassPathPackageInfogetPackageInfo(java.lang.String pkgName)

        return cache.get(pkgName);
    
private static booleanisToplevelClass(java.lang.String fileName)
Checks if a given file name represents a toplevel class.

        return fileName.indexOf('$") < 0;
    
private voidscanForApkFiles(java.io.File source, java.lang.String packageName, java.util.Set classNames, java.util.Set subpackageNames)

        if (source.getPath().endsWith(".apk")) {
            findClassesInApk(source.getPath(), packageName, classNames, subpackageNames);
        } else {
            File[] files = source.listFiles();
            if (files != null) {
                for (File file : files) {
                    scanForApkFiles(file, packageName, classNames, subpackageNames);
                }
            }
        }
    
public static voidsetApkPaths(java.lang.String[] apkPaths)

        ClassPathPackageInfoSource.apkPaths = apkPaths;
    
public voidsetClassLoader(java.lang.ClassLoader classLoader)

        this.classLoader = classLoader;