ClassPathPackageInfoSourcepublic 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 ClassPathPackageInfo | createPackageInfo(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 void | findClasses(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 void | findClassesInApk(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 void | findClassesInDirectory(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 void | findClassesInJar(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.String | getClassName(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.Set | getJarEntries(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 ClassPathPackageInfo | getPackageInfo(java.lang.String pkgName)
return cache.get(pkgName);
| private static boolean | isToplevelClass(java.lang.String fileName)Checks if a given file name represents a toplevel class.
return fileName.indexOf('$") < 0;
| private void | scanForApkFiles(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 void | setApkPaths(java.lang.String[] apkPaths)
ClassPathPackageInfoSource.apkPaths = apkPaths;
| public void | setClassLoader(java.lang.ClassLoader classLoader)
this.classLoader = classLoader;
|
|