package codeStatistics.actions;
import input.Configuration;
import input.ConfigurationReader;
import input.Pattern;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.dom.DOMSource;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.ItemTypeFactory;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.WhitespaceStrippingPolicy;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import statistics.StatisticsCollector;
import statistics.StatisticsDetailsFactory;
import ast.XmlVisitor;
import codeStatistics.AnalysisPluginsManager;
import codeStatistics.AnalysisPluginsManager.Summary;
import codeStatistics.CodeStatisticsPlugin;
import codeStatistics.InfoMessageDialog;
import codeStatistics.Utils;
import codeStatistics.preferences.PreferenceConstants;
import codeStatistics.xpathfunctions.AstTextExtensionFunction;
import codeStatistics.xpathfunctions.ExtensionFunctions;
import codeStatistics.xpathfunctions.FunctionUtils;
import codeStatistics.xpathfunctions.GetAstNodeFunction;
/**
* Our sample action implements workbench action delegate. The action proxy will
* be created by the workbench and shown in the UI. When the user tries to use
* the action, this delegate will be created and execution will be delegated to
* it.
*
* @see IWorkbenchWindowActionDelegate
*/
public class CodeStats implements IWorkbenchWindowActionDelegate {
private static final QName CONTEXT_VARIABLE = FunctionUtils.getName("context");
private class PatternSearchRunnable implements IRunnableWithProgress{
private final DocumentBuilder builder;
private final XPathCompiler xpath;
private final GetAstNodeFunction func;
public Exception error;
private Logger logger;
private final ExtensionFunctions extFuncs;
public PatternSearchRunnable(DocumentBuilder builder, XPathCompiler xpath, GetAstNodeFunction func,
ExtensionFunctions extFuncs, Logger logger) {
this.logger = logger;
this.builder = builder;
this.xpath = xpath;
this.func = func;
this.extFuncs = extFuncs;
}
private void processIcu(ICompilationUnit icu, DocumentBuilder builder,
XPathCompiler xpath) throws Exception {
IJavaProject project = icu.getJavaProject();
CompilationUnit cu = Utils.parse(icu);
//analysis
ArrayList<Map<ASTNode, Object>> analysisResult = AnalysisPluginsManager.analyse(cu);
for(int i=0; i < analysisResult.size(); i++)
extFuncs.setAnalysisResult(i, analysisResult.get(i));
XmlVisitor visitor = new XmlVisitor();
cu.accept(visitor);
func.setNodeMapping(visitor);
String xmlDoc = Utils.print(visitor.getDocument());
XdmNode sourceDoc = builder.build(new DOMSource(visitor.getDocument())); // builder.build(new
// StreamSource(new
// StringReader(xmlDoc)));
logger.fine("Compilation unit is " + xmlDoc);
Set<ASTNode> allMatchingNodes = findMatches(xmlDoc, configuration.allCasesDescriptor, visitor, sourceDoc, xpath).keySet();
Set<ASTNode> notCategorized = new HashSet<ASTNode>(allMatchingNodes);
for (Pattern description : configuration.categorizedCases) {
Map<ASTNode, String> matchingNodes = null;
try {
matchingNodes = findMatches(xmlDoc, description, visitor, sourceDoc, xpath);
} catch (Exception e) {
System.out.println("ERROR in XPath: " + description.name + ", file:" + cu.getJavaElement().getPath().toString());
func.setNodeMapping(null);
throw e;
}
for (Map.Entry<ASTNode, String> entry: matchingNodes.entrySet()) {
ASTNode node = entry.getKey();
notCategorized.remove(node);
if (!allMatchingNodes.contains(node)) {
String message = "ERROR: In file "
+ icu.getElementName()
+ " line: "
+ cu.getLineNumber(node.getStartPosition())
+ "\nCategorized case not found in all matched cases:"
+ node.toString();
logger.info(message);
throw new RuntimeException(message);
}
statsCollector.add(project, cu, description.name, node, entry.getValue());
}
}
for (ASTNode node : notCategorized) {
statsCollector.add(project, cu, "notCategorized", node, null);
}
for (ASTNode node : allMatchingNodes) {
statsCollector.add(project, cu, "ALL: " + configuration.allCasesDescriptor.name, node, null);
}
func.setNodeMapping(null);
for(int i=0; i < analysisResult.size(); i++)
extFuncs.setAnalysisResult(i, null);
}
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
try {
List<ICompilationUnit> allTasks = Utils.getSelectedIcu(selection);
monitor.beginTask("Executing pattern search", allTasks.size());
for (ICompilationUnit icu : allTasks) {
monitor.subTask("Processing: "+ icu.getParent().getElementName() + "." + icu.getElementName());
processIcu(icu, builder, xpath);
monitor.worked(1);
}
} catch (JavaModelException e) {
error = e;
} catch (Exception e) {
error = e;
} finally {
monitor.done();
}
}
}
private IWorkbenchWindow window;
private ITreeSelection selection;
private StatisticsCollector statsCollector;
private Configuration configuration;
private Logger logger = Logger.getLogger("codeStats");
private FileHandler logFileHandler;
private XPathCompiler outputXPathCompiler;
/**
* The constructor.
*/
public CodeStats() {
statsCollector = new StatisticsCollector(
StatisticsDetailsFactory.TOTAL_COUNT);
}
private XPathSelector compileExpr(XPathCompiler xpath, String expression) throws SaxonApiException {
try {
return xpath.compile(expression).load();
} catch (SaxonApiException e) {
System.err.println("Error compiling: '" + expression +"'");
throw e;
}
}
private Map<ASTNode, String> findMatches(String xmlDoc, Pattern description,
XmlVisitor visitor, XdmNode sourceDoc, XPathCompiler xpath)
throws SaxonApiException {
Map<ASTNode, String> result = new HashMap<ASTNode, String>();
String expression = description.xpath;
XPathSelector selector = compileExpr(xpath, expression);
selector.setContextItem(sourceDoc);
XPathSelector outputBeforeSelector = null;
if (description.outputBefore != null) {
outputBeforeSelector = outputXPathCompiler.compile(description.outputBefore).load();
}
for (XdmItem item : selector) {
String valueToInsertBefore = null;
ASTNode node = Utils.getMappedAstNode(visitor, item);
if (node == null)
throw new RuntimeException("Found node is not in AST: " + item);
if (outputBeforeSelector != null) {
outputBeforeSelector.setContextItem(item);
outputBeforeSelector.setVariable(CONTEXT_VARIABLE, item);
valueToInsertBefore = outputBeforeSelector.evaluate().toString();
}
result.put(node, valueToInsertBefore);
}
return result;
}
private void doRun(IAction action) throws Exception {
if (selection == null){
MessageDialog.openInformation(window.getShell(), "Information",
"No input files selected");
return;
}
// Remove old handler
if (logFileHandler != null) {
logFileHandler.flush();
logFileHandler.close();
logger.removeHandler(logFileHandler);
}
// Set new logging parameters
logger.setLevel(getLoggerLevel());
statsCollector = new StatisticsCollector(getStatsLevel());
logFileHandler = new FileHandler(CodeStatisticsPlugin.getDefault()
.getPluginPreferences().getString(
PreferenceConstants.P_LOG_FILE), true);
logFileHandler.setFormatter(new java.util.logging.SimpleFormatter());
logger.addHandler(logFileHandler);
String xpathFile = CodeStatisticsPlugin.getDefault()
.getPluginPreferences().getString(
PreferenceConstants.P_XPATH_FILE);
configuration = ConfigurationReader.loadConfiguration(xpathFile);
Set<Object> roots = new HashSet<Object>();
for (TreePath path : selection.getPaths()) {
roots.add(path.getFirstSegment());
}
Processor proc = new Processor(false);
ItemTypeFactory fact = new ItemTypeFactory(proc);
GetAstNodeFunction func = new GetAstNodeFunction(fact);
proc.registerExtensionFunction(func);
proc.registerExtensionFunction(new AstTextExtensionFunction(fact));
AnalysisPluginsManager.initialiseExtensions();
ExtensionFunctions extFuncs = new ExtensionFunctions(fact, proc);
// XPathCompiler xpath = proc.newXPathCompiler();
// xpath.declareNamespace("cs", FunctionUtils.NAMESPACE);
DocumentBuilder builder = proc.newDocumentBuilder();
builder.setLineNumbering(true);
builder.setWhitespaceStrippingPolicy(WhitespaceStrippingPolicy.ALL);
//show progress dialog
ProgressMonitorDialog dialog = new ProgressMonitorDialog(window.getShell());
this.outputXPathCompiler = FunctionUtils.createCompiler(proc);
this.outputXPathCompiler.declareVariable(CONTEXT_VARIABLE);
PatternSearchRunnable runnable = new PatternSearchRunnable(builder, FunctionUtils.createCompiler(proc), func,
extFuncs, logger);
dialog.run(true, false, runnable);
if (runnable.error != null){
throw runnable.error;
}
//prepare summary dialog
for(Summary summary: AnalysisPluginsManager.getSummaries())
InfoMessageDialog.openInformation(window.getShell(), "Analysis summary", "Plugin:" + summary.title, summary.message);
logger.info("current content of statsCollector: "
+ statsCollector.toString() + "\n");
}
/**
* The action has been activated. The argument of the method represents the
* 'real' action sitting in the workbench UI.
*
* @see IWorkbenchWindowActionDelegate#run
*/
public void run(IAction action) {
try {
doRun(action);
} catch (Throwable e) {
Utils.displayError(window, e);
}
}
private Level getLoggerLevel() {
String levelName = CodeStatisticsPlugin.getDefault()
.getPluginPreferences().getString(
PreferenceConstants.P_LOG_LEVEL);
if ("ALL".equals(levelName))
return Level.INFO;
return Level.INFO;
}
private int getStatsLevel() {
String levelName = CodeStatisticsPlugin.getDefault()
.getPluginPreferences().getString(
PreferenceConstants.P_LOG_LEVEL);
if ("FULL".equals(levelName))
return StatisticsDetailsFactory.FULL_STATS;
if ("COUNT_FOR_FILE".equals(levelName))
return StatisticsDetailsFactory.COUNT_FOR_FILE;
return StatisticsDetailsFactory.TOTAL_COUNT;
}
/**
* Selection in the workbench has been changed. We can change the state of
* the 'real' action here if we want, but this can only happen after the
* delegate has been created.
*
* @see IWorkbenchWindowActionDelegate#selectionChanged
*/
public void selectionChanged(IAction action, ISelection selection) {
if (selection instanceof ITreeSelection) {
this.selection = (ITreeSelection) selection;
}
}
/**
* We can use this method to dispose of any system resources we previously
* allocated.
*
* @see IWorkbenchWindowActionDelegate#dispose
*/
public void dispose() {
}
/**
* We will cache window object in order to be able to provide parent shell
* for the message dialog.
*
* @see IWorkbenchWindowActionDelegate#init
*/
public void init(IWorkbenchWindow window) {
this.window = window;
}
}