/*******************************************************************************
* Copyright (c) 2010 Michal Antkiewicz.
* 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:
* Michal Antkiewicz - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.stats;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import ca.uwaterloo.gsd.fsml.ecore.FSMLEcoreUtil;
import ca.uwaterloo.gsd.fsml.fsml.Model;
import ca.uwaterloo.gsd.fsml.fsml.provider.FsmlEditPlugin;
import ca.uwaterloo.gsd.fsml.preferences.FSMLPreferenceConstants;
/**
* @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca>
*/
public class Stats {
/**
* Singleton
*/
public static final Stats INSTANCE = new Stats();
private final MessageConsole console = new MessageConsole("FSML", null);
private final MessageConsoleStream consoleStream = console.newMessageStream();
private Stats() {
// add the console
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[] { console });
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
reset();
}
/**
* Whether to log, count and print instances of features
*/
public static boolean logFeatureInstances() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_FEATURE_INSTANCE_ID);
}
boolean logFeatureInstances;
private HashMap<String, Integer> features;
/**
* Whether to log, count and print metamodel annotations
*/
public static boolean logMetamodelAnnotations() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_METAMODEL_ANNOTATIONS_ID);
}
boolean logMetamodelAnnotations;
private HashMap<String, Integer> metamodelAnnotations;
/**
* Whether to log, count and print implementation variants
*/
public static boolean logCodePatternVariants() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_CODE_PATTERN_VARIANTS_ID);
}
boolean logCodePatternVariants;
private HashMap<String, HashMap<String,Integer>> codePatternVariants;
/**
* Whether to log, count and print errors
*/
public static boolean logErrors() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_ERRORS_ID);
}
boolean logErrors;
private HashMap<String, Integer> errors;
/**
* Whether to print messages during the execution
*/
public static boolean logMessages() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_MESSAGES_ID);
}
boolean logMessages;
private HashMap<String, Integer> messages;
/**
* Whether to log, count and print errors
*/
public static boolean logBugs() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.LOG_BUGS_ID);
}
boolean logBugs;
private HashMap<String, Integer> bugs;
/**
* Whether to log, count and print scattering and tangling
*/
public static boolean logScatteringAndTangling() {
return FsmlEditPlugin.getPlugin().getPreferenceStore().getBoolean(FSMLPreferenceConstants.COMPUTE_SCATTERING_AND_TANGLING_MEASURES_ID);
}
public boolean logScatteringAndTangling;
private HashMap<String, HashMap<String, Integer>> scatteringAndTangling;
public void reset() {
features = new HashMap<String, Integer>();
metamodelAnnotations = new HashMap<String, Integer>();
codePatternVariants = new HashMap<String, HashMap<String,Integer>>();
errors = new HashMap<String, Integer>();
messages = new HashMap<String, Integer>();
bugs = new HashMap<String, Integer>();
scatteringAndTangling = new HashMap<String, HashMap<String, Integer>>();
console.clearConsole();
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
logBugs = logBugs();
logCodePatternVariants = logCodePatternVariants();
logErrors = logErrors();
logFeatureInstances = logFeatureInstances();
logMessages = logMessages();
logMetamodelAnnotations = logMetamodelAnnotations();
logScatteringAndTangling = logScatteringAndTangling();
}
private void logAndIncrement(String key, HashMap<String, Integer> map) {
Integer count = map.get(key);
if (count == null)
map.put(key, 1);
else
map.put(key, count + 1);
}
public void logFeatureInstance(String feature) {
if (!logFeatureInstances)
return;
logAndIncrement(feature, features);
}
public String computeFeatureId(EObject element, EStructuralFeature eStructuralFeature) {
StringBuffer pathToRoot = new StringBuffer();
EObject aux = element;
while (aux.eContainingFeature() != null) {
pathToRoot.insert(0, aux.eContainingFeature().getName() + " ");
aux = aux.eContainer();
}
return pathToRoot + (eStructuralFeature != null ? eStructuralFeature.getName() : "" );
}
public void logFeatureInstance(EObject element, EStructuralFeature eStructuralFeature, EAnnotation annotation) {
logFeatureInstance(
computeFeatureId(element, eStructuralFeature) +
(annotation != null ? " <" + annotation.getSource() + ">" : "" )
);
}
public void logImplVariant (String feature, String implVariant) {
if (!codePatternVariants.containsKey(feature)) {
HashMap<String, Integer> implVariants = new HashMap<String, Integer>();
implVariants.put(implVariant, new Integer(1));
codePatternVariants.put(feature, implVariants);
}
else {
HashMap<String, Integer> implVariants = codePatternVariants.get(feature);
if (!implVariants.containsKey(implVariant)) {
implVariants.put(implVariant, new Integer(1));
codePatternVariants.put(feature,implVariants);
}
else {
Integer count = implVariants.get(implVariant);
count++;
implVariants.put(implVariant, count);
codePatternVariants.put(feature,implVariants);
}
}
}
public void logImplVariant(EClass eClass, EStructuralFeature eStructuralFeature, EAnnotation annotation, String implVariant) {
logImplVariant(eClass.getName() + "::" + eStructuralFeature.getName() + " <" + annotation.getSource() + ">", implVariant);
}
public void logError(String error) {
if (!logErrors)
return;
logAndIncrement(error, errors);
// Michal: output errors to the debug console anyway
System.out.println("Error: " + error);
}
public void printMessage(String message) {
if (logMessages)
consoleStream.println(message);
// Michal: output messages to the debug console anyway
System.out.println("Message: " + message);
}
public void logMessage(String message) {
if (!logMessages)
return;
logAndIncrement(message, messages);
}
public void logBug(String message) {
logAndIncrement(message, bugs);
// Michal: output bugs to the debug console anyway
System.out.println("Bug: " + message);
}
public void logMetamodelAnnotation(EModelElement modelElement) {
if (!logMetamodelAnnotations)
return;
ArrayList<EAnnotation> annotations = FSMLEcoreUtil.getAllEAnnotations(modelElement);
for (Object object : annotations) {
EAnnotation annotation = (EAnnotation) object;
String key = annotation.getSource();
logAndIncrement(key, metamodelAnnotations);
}
}
public void logScatteringAndTangling(EObject element, EStructuralFeature feature, Object value, String location) {
if (!logScatteringAndTangling)
return;
if (element instanceof Model)
return;
// navigate to the concern, i.e., concept contained by the model
EObject concern = element;
String concernId = null;
while (concern.eContainer() != null) {
EClass eClass = concern.eClass();
EReference baseConceptReference = FSMLEcoreUtil.findBaseConceptReference(eClass);
if (baseConceptReference != null) {
concern = (EObject) concern.eGet(baseConceptReference);
eClass = concern.eClass();
}
EStructuralFeature fqNameFeature = FSMLEcoreUtil.findEStructuralFeatureWithAnnotations(eClass, new String[] { "fullyQualifiedName" });
if (fqNameFeature != null) {
concernId = (String) concern.eGet(fqNameFeature);
break;
}
else {
EStructuralFeature nameFeature = FSMLEcoreUtil.findEStructuralFeatureWithAnnotations(eClass, new String[] { "className" });
EStructuralFeature qualifierFeature = FSMLEcoreUtil.findEStructuralFeatureWithAnnotations(eClass, new String[] { "qualifier" });
if (nameFeature != null) {
String name = (String) concern.eGet(nameFeature);
String qualifier = (String) concern.eGet(qualifierFeature);
concernId = qualifier != null ?
qualifier + "." + name :
name;
break;
}
}
concern = concern.eContainer();
}
if (concernId == null)
return;
HashMap<String, Integer> locationsOfConcern = scatteringAndTangling.get(concernId);
if (locationsOfConcern == null) {
locationsOfConcern = new HashMap<String, Integer>();
locationsOfConcern.put(location, 1);
scatteringAndTangling.put(concernId, locationsOfConcern);
}
else
logAndIncrement(location, locationsOfConcern);
}
public void printScatteringAndTangling() {
if (! logScatteringAndTangling)
return;
consoleStream.print("\nScattering and tangling: ");
StringBuffer buffer = new StringBuffer();
if (scatteringAndTangling.size() > 0)
buffer.append(": (" + scatteringAndTangling.size() + ")\n");
HashMap<String, List<String>> components = new HashMap<String, List<String>>();
HashMap<String, List<String>> operations = new HashMap<String, List<String>>();
int numberOfConcerns = 0;
for (Entry<String, HashMap<String, Integer>> entry : scatteringAndTangling.entrySet()) {
String concern = entry.getKey();
numberOfConcerns++;
for (Entry<String, Integer> locationEntry : entry.getValue().entrySet()) {
String location = locationEntry.getKey();
buffer.append(concern + "\t" + location + "\t" + locationEntry.getValue() + "\n");
if (location.endsWith("()")) {
List<String> ops = operations.get(concern);
if (ops == null) {
ops = new LinkedList<String>();
operations.put(concern, ops);
}
if (!ops.contains(location))
ops.add(location);
}
else {
List<String> comps = components.get(concern);
if (comps == null) {
comps = new LinkedList<String>();
components.put(concern, comps);
}
if (!comps.contains(location))
comps.add(location);
}
}
}
consoleStream.println(buffer.toString());
// print metrics: concern diffusion over components and over operations
buffer = new StringBuffer();
// sort into buckets
HashMap<Integer, Integer> componentBuckets = new HashMap<Integer, Integer>();
HashMap<Integer, Integer> operationBuckets = new HashMap<Integer, Integer>();
int maxNumberOfComponents = 0;
int maxNumberOfOperations = 0;
buffer.append("Concern diffusion over components.\n");
for (Entry<String, List<String>> entry : components.entrySet()) {
int numberOfComponents = entry.getValue().size();
if (numberOfComponents > maxNumberOfComponents)
maxNumberOfComponents = numberOfComponents;
buffer.append(entry.getKey() + "\t" + numberOfComponents + "\t" + entry.getValue().toString() + "\n");
Integer count = componentBuckets.get(numberOfComponents);
componentBuckets.put(numberOfComponents, count == null ? 1 : count + 1);
}
buffer.append("\nConcern diffusion over operations.\n");
for (Entry<String, List<String>> entry : operations.entrySet()) {
int numberOfOperations = entry.getValue().size();
if (numberOfOperations > maxNumberOfOperations)
maxNumberOfOperations = numberOfOperations;
buffer.append(entry.getKey() + "\t" + numberOfOperations + "\t" + entry.getValue().toString() + "\n");
Integer count = operationBuckets.get(numberOfOperations);
operationBuckets.put(numberOfOperations, count == null ? 1 : count + 1);
}
consoleStream.println(buffer.toString());
// print distrubution over buckets
StringBuffer bucketBuffer = new StringBuffer("Number of components\t");
StringBuffer countBuffer = new StringBuffer("Number of concerns\t");
StringBuffer percentageBuffer = new StringBuffer("Percentage of all\t");
for (int i = 1; i <= maxNumberOfComponents; i++) {
Integer count = componentBuckets.get(i);
if (count == null)
continue;
bucketBuffer.append(i + "\t");
countBuffer.append(count + "\t");
percentageBuffer.append(count * 100 / numberOfConcerns + "%\t");
}
consoleStream.println("Distribution of " + numberOfConcerns + " concerns over components (CDC)");
consoleStream.println(bucketBuffer.toString());
consoleStream.println(countBuffer.toString());
consoleStream.println(percentageBuffer.toString());
bucketBuffer = new StringBuffer("Number of operations\t");
countBuffer = new StringBuffer("Number of concerns\t");
percentageBuffer = new StringBuffer("Percentage of all\t");
for (int i = 1; i <= maxNumberOfOperations; i++) {
Integer count = operationBuckets.get(i);
if (count == null)
continue;
bucketBuffer.append(i + "\t");
countBuffer.append(count + "\t");
percentageBuffer.append(count * 100 / numberOfConcerns + "%\t");
}
consoleStream.println("Distribution of " + numberOfConcerns + " concerns over operations (CDO)");
consoleStream.println(bucketBuffer.toString());
consoleStream.println(countBuffer.toString());
consoleStream.println(percentageBuffer.toString());
}
public void printEntries(HashMap<String, Integer> map) {
StringBuffer buffer = new StringBuffer();
if (map.size() > 0)
buffer.append(": (" + map.size() + ")\n");
for (Entry<String, Integer> entry : map.entrySet())
buffer.append(entry.getKey() + "\t" + entry.getValue().toString() + "\n");
consoleStream.println(buffer.toString());
}
public void printFeatureInstances() {
if (!logFeatureInstances)
return;
consoleStream.print("\n" + (features.isEmpty() ? "No feature instances." : "Feature instances"));
printEntries(features);
}
public void printMetamodelAnnotations() {
if (!logMetamodelAnnotations)
return;
consoleStream.print("\nMetamodel annotations");
printEntries(metamodelAnnotations);
}
public void printErrors() {
if (!logErrors())
return;
consoleStream.print("\n" + (errors.isEmpty() ? "No errors." : "Errors"));
printEntries(errors);
}
public void printImplVariants() {
if (!logCodePatternVariants)
return;
consoleStream.print("\n" + (codePatternVariants.isEmpty() ? "No code pattern variants." : "Code pattern variants:\n"));
StringBuffer buffer = new StringBuffer();
for (Entry<String, HashMap<String, Integer>> entry: codePatternVariants.entrySet()) {
buffer.append(entry.getKey() + "\n");
for (Entry<String, Integer>entry2:entry.getValue().entrySet()) {
buffer.append("\t"+entry2.getKey() + "\t" + entry2.getValue().toString() + "\n");
}
}
consoleStream.println(buffer.toString());
}
public void printCountedMessages() {
if (!logMessages)
return;
consoleStream.print("\n" + (messages.isEmpty() ? "No messages." : "Messages"));
printEntries(messages);
}
public void printBugs() {
if (!logBugs)
return;
consoleStream.print("\n" + (bugs.isEmpty() ? "No bugs." : "Bugs"));
printEntries(bugs);
}
public void printAll() {
printMetamodelAnnotations();
printFeatureInstances();
printErrors();
printImplVariants();
printCountedMessages();
printBugs();
printScatteringAndTangling();
}
}