/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.util.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.net.URI;
import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
import com.ibm.wala.classLoader.Module;
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.util.strings.Atom;
/**
* Reads {@link AnalysisScope} from a text file.
*/
public class AnalysisScopeReader {
private static final ClassLoader MY_CLASSLOADER = AnalysisScopeReader.class.getClassLoader();
protected static final String BASIC_FILE = "primordial.txt";
/**
* read in an analysis scope for a Java application from a text file
* @param scopeFileName the text file specifying the scope
* @param exclusionsFile a file specifying code to be excluded from the scope; can be <code>null</code>
* @param javaLoader the class loader used to read in files referenced in the scope file, via {@link ClassLoader#getResource(String)}
* @return the analysis scope
* @throws IOException
*/
public static AnalysisScope readJavaScope(String scopeFileName, File exclusionsFile, ClassLoader javaLoader) throws IOException {
AnalysisScope scope = AnalysisScope.createJavaAnalysisScope();
return read(scope, scopeFileName, exclusionsFile, javaLoader, new FileProvider());
}
public static AnalysisScope read(AnalysisScope scope, String scopeFileName, File exclusionsFile, ClassLoader javaLoader,
FileProvider fp) throws IOException {
BufferedReader r = null;
try {
// Now reading from jar is included in WALA, but we can't use their version, because they load from
// jar by default and use filesystem as fallback. We want it the other way round. E.g. to deliver default
// configuration files with the jar, but use userprovided ones if present in the working directory.
// InputStream scopeFileInputStream = fp.getInputStreamFromClassLoader(scopeFileName, javaLoader);
File scopeFile = new File(scopeFileName);
String line;
// assume the scope file is UTF-8 encoded; ASCII files will also be handled properly
// TODO allow specifying encoding as a parameter?
if (scopeFile.exists()) {
r = new BufferedReader(new InputStreamReader(new FileInputStream(scopeFile), "UTF-8"));
} else {
// try to read from jar
InputStream inFromJar = javaLoader.getResourceAsStream(scopeFileName);
if (inFromJar == null) {
throw new IllegalArgumentException("Unable to retreive " + scopeFileName + " from the jar using " + javaLoader);
}
r = new BufferedReader(new InputStreamReader(inFromJar));
}
while ((line = r.readLine()) != null) {
processScopeDefLine(scope, javaLoader, line);
}
if (exclusionsFile != null) {
InputStream fs = exclusionsFile.exists()? new FileInputStream(exclusionsFile): FileProvider.class.getClassLoader().getResourceAsStream(exclusionsFile.getName());
scope.setExclusions(new FileOfClasses(fs));
}
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return scope;
}
protected static AnalysisScope read(AnalysisScope scope, final URI scopeFileURI, final File exclusionsFile, ClassLoader javaLoader,
FileProvider fp) throws IOException {
BufferedReader r = null;
try {
String line;
final InputStream inStream = scopeFileURI.toURL().openStream();
if (inStream == null) {
throw new IllegalArgumentException("Unable to retrieve URI " + scopeFileURI.toString());
}
r = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
while ((line = r.readLine()) != null) {
processScopeDefLine(scope, javaLoader, line);
}
if (exclusionsFile != null) {
final InputStream fs = exclusionsFile.exists()
? new FileInputStream(exclusionsFile)
: FileProvider.class.getClassLoader().getResourceAsStream(exclusionsFile.getName());
scope.setExclusions(new FileOfClasses(fs));
}
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return scope;
}
public static void processScopeDefLine(AnalysisScope scope, ClassLoader javaLoader, String line) throws IOException {
if (line == null) {
throw new IllegalArgumentException("null line");
}
StringTokenizer toks = new StringTokenizer(line, "\n,");
if (!toks.hasMoreTokens()) {
return;
}
Atom loaderName = Atom.findOrCreateUnicodeAtom(toks.nextToken());
ClassLoaderReference walaLoader = scope.getLoader(loaderName);
@SuppressWarnings("unused")
String language = toks.nextToken();
String entryType = toks.nextToken();
String entryPathname = toks.nextToken();
FileProvider fp = (new FileProvider());
if ("classFile".equals(entryType)) {
File cf = fp.getFile(entryPathname, javaLoader);
try {
scope.addClassFileToScope(walaLoader, cf);
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE(e.toString());
}
} else if ("sourceFile".equals(entryType)) {
File sf = fp.getFile(entryPathname, javaLoader);
scope.addSourceFileToScope(walaLoader, sf, entryPathname);
} else if ("binaryDir".equals(entryType)) {
File bd = fp.getFile(entryPathname, javaLoader);
assert bd.isDirectory();
scope.addToScope(walaLoader, new BinaryDirectoryTreeModule(bd));
} else if ("sourceDir".equals(entryType)) {
File sd = fp.getFile(entryPathname, javaLoader);
assert sd.isDirectory();
scope.addToScope(walaLoader, new SourceDirectoryTreeModule(sd));
} else if ("jarFile".equals(entryType)) {
Module M = fp.getJarFileModule(entryPathname, javaLoader);
scope.addToScope(walaLoader, M);
} else if ("loaderImpl".equals(entryType)) {
scope.setLoaderImpl(walaLoader, entryPathname);
} else if ("stdlib".equals(entryType)) {
String[] stdlibs = WalaProperties.getJ2SEJarFiles();
for (int i = 0; i < stdlibs.length; i++) {
scope.addToScope(walaLoader, new JarFile(stdlibs[i]));
}
} else {
Assertions.UNREACHABLE();
}
}
/**
* @param exclusionsFile file holding class hierarchy exclusions. may be null
* @throws IOException
* @throws IllegalStateException if there are problmes reading wala properties
*/
public static AnalysisScope makePrimordialScope(File exclusionsFile) throws IOException {
return readJavaScope(BASIC_FILE, exclusionsFile, MY_CLASSLOADER);
}
/**
* @param classPath class path to analyze, delimited by {@link File#pathSeparator}
* @param exclusionsFile file holding class hierarchy exclusions. may be null
* @throws IOException
* @throws IllegalStateException if there are problems reading wala properties
*/
public static AnalysisScope makeJavaBinaryAnalysisScope(String classPath, File exclusionsFile) throws IOException {
if (classPath == null) {
throw new IllegalArgumentException("classPath null");
}
AnalysisScope scope = makePrimordialScope(exclusionsFile);
ClassLoaderReference loader = scope.getLoader(AnalysisScope.APPLICATION);
addClassPathToScope(classPath, scope, loader);
return scope;
}
public static void addClassPathToScope(String classPath, AnalysisScope scope, ClassLoaderReference loader) {
if (classPath == null) {
throw new IllegalArgumentException("null classPath");
}
try {
StringTokenizer paths = new StringTokenizer(classPath, File.pathSeparator);
while (paths.hasMoreTokens()) {
String path = paths.nextToken();
if (path.endsWith(".jar")) {
JarFile jar = new JarFile(path);
scope.addToScope(loader, jar);
try {
if (jar.getManifest() != null) {
String cp = jar.getManifest().getMainAttributes().getValue("Class-Path");
if (cp != null) {
for(String cpEntry : cp.split(" ")) {
addClassPathToScope(new File(path).getParent() + File.separator + cpEntry, scope, loader);
}
}
}
} catch (RuntimeException e) {
System.err.println("warning: trouble processing class path of " + path);
}
} else {
File f = new File(path);
if (f.isDirectory()) {
scope.addToScope(loader, new BinaryDirectoryTreeModule(f));
} else {
scope.addClassFileToScope(loader, f);
}
}
}
} catch (IOException e) {
Assertions.UNREACHABLE(e.toString());
} catch (InvalidClassFileException e) {
Assertions.UNREACHABLE(e.toString());
}
}
}