/*
* AnalysisManager.java - This file is part of the Jakstab project.
* Copyright 2007-2015 Johannes Kinder <jk@jakstab.org>
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*/
package org.jakstab;
import java.io.File;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jakstab.analysis.ConfigurableProgramAnalysis;
import org.jakstab.util.Logger;
public class AnalysisManager {
private static final Logger logger = Logger.getLogger(AnalysisManager.class);
private static class SingletonHolder {
public static final AnalysisManager INSTANCE = new AnalysisManager();
}
public static AnalysisManager getInstance() {
return SingletonHolder.INSTANCE;
}
private Map<Character, Class<? extends ConfigurableProgramAnalysis>> shortHandMap;
private Map<Class<? extends ConfigurableProgramAnalysis>, AnalysisProperties> analysisProperties;
private Map<Class<? extends ConfigurableProgramAnalysis>, ConfigurableProgramAnalysis> analysisInstances;
private AnalysisManager() {
shortHandMap = new HashMap<Character, Class<? extends ConfigurableProgramAnalysis>>();
analysisProperties = new HashMap<Class<? extends ConfigurableProgramAnalysis>, AnalysisProperties>();
analysisInstances = new HashMap<Class<? extends ConfigurableProgramAnalysis>, ConfigurableProgramAnalysis>();
// Enumerate all analyses and register them
String pkg = "org.jakstab.analysis";
File dir = new File(Options.jakstabHome + "/bin/" + pkg.replace(".", "/"));
List<Class<? extends ConfigurableProgramAnalysis>> classes = findCPAClasses(dir, pkg);
for(Class<? extends ConfigurableProgramAnalysis> cpaClass : classes) {
AnalysisProperties aProps = new AnalysisProperties();
try {
//logger.debug("Trying to register " + cpaClass.getSimpleName());
cpaClass.getMethod("register", AnalysisProperties.class).invoke(cpaClass, aProps);
if (aProps.getShortHand() != ' ') {
if (shortHandMap.containsKey(aProps.getShortHand())) {
logger.fatal("Duplicate short hand '" + aProps.getShortHand() + "' registered by " +
shortHandMap.get(aProps.getShortHand()).getSimpleName() + " and " + cpaClass.getSimpleName() + ".");
}
shortHandMap.put(aProps.getShortHand(), cpaClass);
}
analysisProperties.put(cpaClass, aProps);
// apply properties
} catch (Exception e) {
logger.warn("Failed to register " + cpaClass.getSimpleName());
if (e instanceof RuntimeException)
e.printStackTrace();
}
}
}
public ConfigurableProgramAnalysis createAnalysis(char shortHand) {
Class<? extends ConfigurableProgramAnalysis> cpaClass = shortHandMap.get(shortHand);
if (cpaClass == null)
return null;
try {
ConfigurableProgramAnalysis cpaInstance = cpaClass.newInstance();
analysisInstances.put(cpaClass, cpaInstance);
return cpaInstance;
} catch (InstantiationException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public char getShorthand(Class<? extends ConfigurableProgramAnalysis> clazz) {
AnalysisProperties prop = analysisProperties.get(clazz);
assert prop != null : "Analysis not registered";
return prop.getShortHand();
}
/**
* Get a previously instantiated analysis instance.
*
* @param clazz the class of the analysis to get
* @return the instance of clazz most recently instantiated by this analysis manager
*/
public ConfigurableProgramAnalysis getAnalysis(Class<? extends ConfigurableProgramAnalysis> clazz) {
return analysisInstances.get(clazz);
}
public String getName(char shortHand) {
Class<? extends ConfigurableProgramAnalysis> cpaClass = shortHandMap.get(shortHand);
if (cpaClass == null)
return null;
return analysisProperties.get(cpaClass).getName();
}
public String getDescription(char shortHand) {
Class<? extends ConfigurableProgramAnalysis> cpaClass = shortHandMap.get(shortHand);
if (cpaClass == null)
return null;
return analysisProperties.get(cpaClass).getDescription();
}
public AnalysisProperties getProperties(ConfigurableProgramAnalysis cpa) {
return analysisProperties.get(cpa.getClass());
}
public String getShorthandsString() {
char[] shds = new char[shortHandMap.size()];
int i = 0;
for (Character c : shortHandMap.keySet()) {
shds[i++] = c;
}
Arrays.sort(shds);
return new String(shds);
}
private static List<Class<? extends ConfigurableProgramAnalysis>> findCPAClasses(File directory, String packageName) {
List<Class<? extends ConfigurableProgramAnalysis>> classes = new ArrayList<Class<? extends ConfigurableProgramAnalysis>>();
for (File file : directory.listFiles()) {
String fileName = file.getName();
if (file.isDirectory()) {
assert !fileName.contains(".");
classes.addAll(findCPAClasses(file, packageName + "." + fileName));
} else if (fileName.endsWith(".class")) {
Class<?> clazz;
try {
try {
clazz = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6));
} catch (ExceptionInInitializerError e) {
clazz = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6),
false, Thread.currentThread().getContextClassLoader());
}
if (ConfigurableProgramAnalysis.class.isAssignableFrom(clazz)) {
int mod = clazz.getModifiers();
if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))
classes.add(clazz.asSubclass(ConfigurableProgramAnalysis.class));
}
} catch (ClassNotFoundException e) {
logger.warn("Could not load class " + packageName + "." + fileName);
}
}
}
return classes;
}
}