blob: cbf14ad0bac4ffa2944da9b8df3811528ca5c4e8 [file] [log] [blame]
/**
* <copyright>
* Copyright (c) 2010-2016 Henshin developers. 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
* </copyright>
*/
package org.eclipse.emf.henshin.multicda.cpa;
import java.io.File;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.model.Edge;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.Module;
import org.eclipse.emf.henshin.model.NestedCondition;
import org.eclipse.emf.henshin.model.Unit;
import org.eclipse.emf.henshin.model.exporters.HenshinAGGExporter;
import org.eclipse.emf.henshin.model.impl.EdgeImpl;
import org.eclipse.emf.henshin.multicda.cpa.importer.AggHenshinCriticalPairTranslator;
import org.eclipse.emf.henshin.multicda.cpa.result.CPAResult;
import org.eclipse.emf.henshin.multicda.cpa.result.CriticalPair;
import agg.parser.ConflictsDependenciesContainer;
import agg.parser.CriticalPairOption;
import agg.parser.DependencyPairContainer;
import agg.parser.ExcludePairContainer;
import agg.parser.ParserFactory;
import agg.util.XMLHelper;
import agg.xt_basis.GraGra;
import agg.xt_basis.GraTraOptions;
import agg.xt_basis.MorphCompletionStrategy;
import agg.xt_basis.Rule;
/**
* The Implementation of the critical pair analysis for the <code>ICriticalPairAnalysis</code> interface by using the
* analysis implemented in AGG.
*
* @author Kristopher Born
*
*/
public class CpaByAGG implements ICriticalPairAnalysis {
GraGra gragra;
Module module;
AggHenshinCriticalPairTranslator importer;
private boolean monitorProgress = false;
private IProgressMonitor progressMonitor;
Set<org.eclipse.emf.henshin.model.Rule> firstHenshinRuleSetForAnalysis;
Set<org.eclipse.emf.henshin.model.Rule> secondHenshinRuleSetForAnalysis;
Set<agg.xt_basis.Rule> firstAggRuleSetForAnalysis;
Set<agg.xt_basis.Rule> secondAggRuleSetForAnalysis;
String workingPathWOExtension;
CDAOptions options;
private boolean generateCpxFile = false;
private boolean createRuleParameterForAllAttributes = true;
private String resultDirectory = File.separator.concat("files").concat(File.separator).concat("results")
.concat(File.separator);
private String debugFileName = "debug.ggx";
File pathToProject = new File("");
File aggDebugFile = new File(
pathToProject.getAbsolutePath().concat(File.separator).concat(resultDirectory).concat(debugFileName));
/**
* Default constructor.
*/
public CpaByAGG() {
}
/**
* A constructor which allows to store the result of the critical pair analysis by AGG within AGGs format.
*
* @param debugFile The File for storing the result of the critical pair analysis by AGG within AGGs format.
*/
public CpaByAGG(File debugFile) {
generateCpxFile = true;
aggDebugFile = debugFile;
}
/**
* Initializes the critical pair calculation with the <code>rules</code> parameter, used as first and second rules
* as well as the <code>options</code>. Even performs a check to ensure the parameters full fill the requirements to
* perform the analysis.
*
* @param rules The rules for which the critical pair analysis shall be executed.
* @param options The options settings that shall be applied on the calculation of the critical pairs
* @throws UnsupportedRuleException in case of invalid rules.
*/
@Override
public void init(Set<org.eclipse.emf.henshin.model.Rule> rules, CDAOptions options)
throws UnsupportedRuleException {
init(rules, rules, options);
}
/**
* Initializes the critical pair calculation with <code>r1</code> as first rules and <code>r2</code> as second rules
* of the critical pairs as well as the <code>options</code>. Even performs a check to ensure the parameters full
* fill the requirements to perform the analysis.
*
* @param r1 the first rules for the critical pair analysis.
* @param r2 the second rules for the critical pair analysis.
* @param options the options settings that shall be applied on the calculation of the critical pairs.
* @throws UnsupportedRuleException in case of invalid rules.
*/
@Override
public void init(Set<org.eclipse.emf.henshin.model.Rule> r1, Set<org.eclipse.emf.henshin.model.Rule> r2,
CDAOptions options) throws UnsupportedRuleException {
this.options = options;
this.firstHenshinRuleSetForAnalysis = r1;
this.secondHenshinRuleSetForAnalysis = r2;
if (r1.size() == 0 || r2.size() == 0) {
throw new UnsupportedRuleException(UnsupportedRuleException.noInputRule);
}
// prevents to export a rule twice.
List<org.eclipse.emf.henshin.model.Rule> listOfAllOriginalRulesToBeExported = new LinkedList<org.eclipse.emf.henshin.model.Rule>();
for (org.eclipse.emf.henshin.model.Rule rule : r1) {
if (!listOfAllOriginalRulesToBeExported.contains(rule))
listOfAllOriginalRulesToBeExported.add(rule);
}
for (org.eclipse.emf.henshin.model.Rule rule : r2) {
if (!listOfAllOriginalRulesToBeExported.contains(rule))
listOfAllOriginalRulesToBeExported.add(rule);
}
Module originalModuleOfFirstRule = null;
org.eclipse.emf.henshin.model.Rule ruleForModuleCopy = null;
//find a rule, which provides a useful module
for (org.eclipse.emf.henshin.model.Rule rule : listOfAllOriginalRulesToBeExported) {
if (originalModuleOfFirstRule != null && ruleForModuleCopy != null)
break;
if (rule.eContainer() != null
&& (((Module) rule.eContainer()).getImports().get(0).getEClassifiers().size() > 0)) {
originalModuleOfFirstRule = (Module) rule.eContainer();
ruleForModuleCopy = rule;
}
}
if (originalModuleOfFirstRule == null || ruleForModuleCopy == null) {
System.err.println("ERROR: None of the rules provide a useful Module – no meta-model could be resolved.");
return;
}
// creating a appropriate module for the export, with the aim to keep the provided rules and their modules unchanged
module = (Module) EcoreUtil.copy(ruleForModuleCopy.eContainer());
// since even unsatisfactory copies of the rules and units have been created, those have to be removed.
module.getUnits().clear();
// Only the designated rules are copied and added to the module for the export. The original rules remain unchanged.
for (org.eclipse.emf.henshin.model.Rule rule : listOfAllOriginalRulesToBeExported) {
org.eclipse.emf.henshin.model.Rule copyOfRule = EcoreUtil.copy(rule);
module.getUnits().add(copyOfRule);
module.toString();
}
// checks the rules which shall serve later on for the analysis.
Set<org.eclipse.emf.henshin.model.Rule> rulesToBeChecked = new HashSet<org.eclipse.emf.henshin.model.Rule>();
for (Unit unit : module.getUnits()) {
if (unit instanceof org.eclipse.emf.henshin.model.Rule)
rulesToBeChecked.add((org.eclipse.emf.henshin.model.Rule) unit);
}
check(rulesToBeChecked);
List<org.eclipse.emf.henshin.model.Rule> originalRules = new LinkedList<org.eclipse.emf.henshin.model.Rule>();
originalRules.addAll(firstHenshinRuleSetForAnalysis);
originalRules.addAll(secondHenshinRuleSetForAnalysis);
// its important to instantiate the importer with the original rules, since the created results shall reference elements within the original rules
importer = new AggHenshinCriticalPairTranslator(originalRules);
// // first of all complete the rules by inserting missing EOpposite Edges, since they have to be present in the rule set within AGG
// // the modification of the rules is negligible since we are working with copies of the original rules.
// completeRulesByEOppositeEdges();
// *.ggx file generation
IStatus exportModuleToAGGStatus = exportModuleToAGG(workingPathWOExtension + ".ggx");
if (exportModuleToAGGStatus.getSeverity() == IStatus.ERROR) {
throw new UnsupportedRuleException(
UnsupportedRuleException.exportFailed + " " + exportModuleToAGGStatus.getMessage());
}
String ggxFile = workingPathWOExtension + ".ggx";
gragra = load(ggxFile);
boolean checkDangling = true; // default
boolean injectiveMatching = true; // default
firstAggRuleSetForAnalysis = new HashSet<Rule>();
secondAggRuleSetForAnalysis = new HashSet<Rule>();
for (org.eclipse.emf.henshin.model.Rule rule : r1) {
if (rule != null) {
Rule equivalentAggRule = gragra.getRule(rule.getName());
if (equivalentAggRule != null)
firstAggRuleSetForAnalysis.add(equivalentAggRule);
if (!rule.isCheckDangling())
checkDangling = false;
if (!rule.isInjectiveMatching())
injectiveMatching = false;
}
}
for (org.eclipse.emf.henshin.model.Rule rule : r2) {
if (rule != null) {
Rule equivalentAggRule = gragra.getRule(rule.getName());
if (equivalentAggRule != null)
secondAggRuleSetForAnalysis.add(equivalentAggRule);
if (!rule.isCheckDangling())
checkDangling = false;
if (!rule.isInjectiveMatching())
injectiveMatching = false;
}
}
// set dangling and injective option for AGG
MorphCompletionStrategy morphCompletionStrategy = gragra.getMorphismCompletionStrategy();
if (!checkDangling)
morphCompletionStrategy.removeProperty(GraTraOptions.DANGLING);
if (!injectiveMatching)
morphCompletionStrategy.removeProperty(GraTraOptions.INJECTIVE);
}
// this refactoring of the rules may be extractet into another class and rewritten as an Henshin inplace
// transformation
private void completeRulesByEOppositeEdges() {
// 1. have to be done for all the rules
for (Unit unit : module.getUnits()) {
if (unit instanceof org.eclipse.emf.henshin.model.Rule) {
// 2. all graphs of a rule have to be refactored, to be complete
org.eclipse.emf.henshin.model.Rule rule = (org.eclipse.emf.henshin.model.Rule) unit;
completeGraphByMissingEOppositeEdges(rule.getLhs());
completeGraphByMissingEOppositeEdges(rule.getRhs());
// 3. even the nested conditions have to be completed
EList<NestedCondition> nestedConditionsOfLhs = rule.getLhs().getNestedConditions();
for (NestedCondition nestedCondition : nestedConditionsOfLhs) {
completeGraphByMissingEOppositeEdges(nestedCondition.getConclusion());
}
EList<NestedCondition> nestedConditionsOfRhs = rule.getLhs().getNestedConditions();
for (NestedCondition nestedCondition : nestedConditionsOfRhs) {
completeGraphByMissingEOppositeEdges(nestedCondition.getConclusion());
}
}
}
}
private void completeGraphByMissingEOppositeEdges(Graph graph) {
List<Edge> listOfEdgeWithMissingEOpposites = new LinkedList<Edge>();
for (Edge edge : graph.getEdges()) {
EReference eOppositeTypeOfEdge = edge.getType().getEOpposite();
if (eOppositeTypeOfEdge != null) {
if (edge.getTarget().getOutgoing(eOppositeTypeOfEdge, edge.getSource()) == null) {
// the EOpposite edge does not exist within the graph
listOfEdgeWithMissingEOpposites.add(edge);
}
}
}
for (Edge edgeWithMissingEOpposite : listOfEdgeWithMissingEOpposites) {
new EdgeImpl(edgeWithMissingEOpposite.getTarget(), edgeWithMissingEOpposite.getSource(),
edgeWithMissingEOpposite.getType().getEOpposite());
}
}
/**
* Check for the validity of the rules in regard to the supported features.
*
* @param rules The rules to be checked.
* @return <code>true</code> if the rules are valid for critical pair analysis.
* @throws UnsupportedRuleException in case of invalid rules.
*/
@Override
public boolean check(Set<org.eclipse.emf.henshin.model.Rule> rules) throws UnsupportedRuleException {
return InputDataChecker.getInstance().check(rules);
}
/**
* Saves the result of the <code>HenshinAGGExporter</code>.
*
* @param savePath The path for saving the AGG file of the export process.
* @return
*/
private IStatus exportModuleToAGG(String savePath) {
URI uri = URI.createFileURI(savePath);
HenshinAGGExporter exporter = new HenshinAGGExporter();
// exporter.setCreateRuleParameterForAllAttributes(createRuleParameterForAllAttributes); //reintroduce with
// Henshin 1.3
if (options.essentialCP)
exporter.setExportWithoutUpperLimitsOnTypeGraph(true);
return exporter.doExport(module, uri, options.isIgnoreMultiplicities());
}
/**
* Loads the *.ggx file <code>fName</code> into the <code>GraGra</code> representation
*
* @param fileName input *.ggx file
* @return the <code>GraGra</code> if input was *.ggx else <code>null</code>
*/
private GraGra load(String fileName) {
if (fileName.endsWith(".ggx")) {
XMLHelper h = new XMLHelper();
if (h.read_from_xml(fileName)) {
GraGra gra = new GraGra(true);
h.getTopObject(gra);
gra.setFileName(fileName);
return gra;
}
}
return null;
}
/**
* Starts the calculation of conflicts for the initialized rules and options.
*
* @return a <code>CPAResult</code>, which consists of a set of <code>Conflict</code>s.
*/
@Override
public CPAResult runConflictAnalysis() {
ExcludePairContainer epc = (ExcludePairContainer) ParserFactory.createEmptyCriticalPairs(gragra,
CriticalPairOption.EXCLUDEONLY, false);
epc.enablePACs(true);
CPAResult conflictResult = null;
setOptionsOnContainer(epc, options);
computeCriticalPairs(firstAggRuleSetForAnalysis, secondAggRuleSetForAnalysis, epc);
conflictResult = importer.importExcludePairContainer(epc, options.essentialCP);
if (generateCpxFile)
saveCPAasCPX(aggDebugFile.getAbsolutePath().replaceAll(".ggx", ".cpx"), epc, null);
return conflictResult;
}
public static CPAResult joinCPAResults(CPAResult cr1, CPAResult cr2) {
if (cr1 == null)
return cr2;
if (cr2 == null)
return cr1;
CPAResult cpaResult = new CPAResult();
List<CriticalPair> cp1 = cr1.getCriticalPairs();
for (CriticalPair critPair : cp1) {
cpaResult.addResult(critPair);
}
List<CriticalPair> cp2 = cr2.getCriticalPairs();
for (CriticalPair critPair : cp2) {
cpaResult.addResult(critPair);
}
return cpaResult;
}
/**
* Starts the calculation of the conflicts for the initialized rules and options.
*
* @param monitor a monitor to report the progress of the calculation.
* @return a set of critical pair results which are conflicts.
*/
@Override
public CPAResult runConflictAnalysis(IProgressMonitor monitor) {
progressMonitor = monitor;
monitorProgress = true;
return runConflictAnalysis();
}
/**
* Starts the calculation of the dependencies for the initialized rules and options.
*
* @return a set of critical pair results which are dependencies.
*/
@Override
public CPAResult runDependencyAnalysis() {
DependencyPairContainer dpc = (DependencyPairContainer) ParserFactory.createEmptyCriticalPairs(gragra,
CriticalPairOption.TRIGGER_DEPEND, false/* defaultValue */);
dpc.enablePACs(true);
CPAResult dependencyResult = null;
setOptionsOnContainer(dpc, options);
computeCriticalPairs(firstAggRuleSetForAnalysis, secondAggRuleSetForAnalysis, dpc);
dependencyResult = importer.importExcludePairContainer(dpc, options.essentialCP);
if (generateCpxFile)
saveCPAasCPX(aggDebugFile.getAbsolutePath().replaceAll(".ggx", ".cpx"), null, dpc);
return dependencyResult;
}
/**
* Starts the calculation of the dependencies for the initialized rules and options.
*
* @param monitor a monitor to report the progress of the calculation.
* @return a set of critical pair results which are dependencies.
*/
@Override
public CPAResult runDependencyAnalysis(IProgressMonitor monitor) {
progressMonitor = monitor;
monitorProgress = true;
return runDependencyAnalysis();
}
/**
* compute the critical pair matrix between the two lists. in the case of N rules1 and M rules2, only N*M + M*N
* combinations will be evaluated. (instead of otherwise (n+m)*(m+n) )
*
* @param rules1 first list of rules (horizontal order)
* @param rules2 second list of rules (vertical order)
*/
private void computeCriticalPairs(Set<Rule> rules1, Set<Rule> rules2, ExcludePairContainer exclude) {
if (firstHenshinRuleSetForAnalysis.size() == 0 || secondHenshinRuleSetForAnalysis.size() == 0)
return; // : hier muss mehr/etwas besseres hin als ein reines "return"
if (exclude != null) {
// progress handling
int progressIntervalR1 = 10000;
int progressIntervalR2 = 10000;
if (firstHenshinRuleSetForAnalysis != null && secondHenshinRuleSetForAnalysis != null) {
progressIntervalR1 = 10000 / firstHenshinRuleSetForAnalysis.size(); //: wenn "firstHenshinRuleSetForAnalysis" keine Elemnte enthält kommt es zu einem Fehler
progressIntervalR2 = progressIntervalR1 / secondHenshinRuleSetForAnalysis.size(); //: wenn "secondHenshinRuleSetForAnalysis" keine Elemnte enthält kommt es zu einem Fehler
}
for (Rule r1 : rules1) {
int partialProgress = 0;
for (Rule r2 : rules2) {
try {
// BenchmarkUtility.startTimeMeasurement();
// Long startTime = System.currentTimeMillis(:)
// TimeUnit.MILLISECONDS.
// System.out.println("time: ");
exclude.getCriticalPair(r1, r2, agg.parser.CriticalPair.EXCLUDE, true);
// System.out.println("duration: ");
} catch (Exception ex) {
ex.printStackTrace();
}
if (monitorProgress) {
progressMonitor.worked(progressIntervalR2);
partialProgress += progressIntervalR2;
}
}
if (monitorProgress) {
progressMonitor.worked(progressIntervalR1 - partialProgress);
}
}
}
}
/**
* Saves the analysis result into a <code>*.ggx</code> file.
*
* @param cpaFileName path and name for the desired <code>*.ggx</code> file.
* @param epc The container object for conflict results.
* @param dpc The container object for dependency results.
*
*/
private void saveCPAasCPX(String cpaFileName, ExcludePairContainer epc, DependencyPairContainer dpc) {
ConflictsDependenciesContainer cDC = new ConflictsDependenciesContainer(epc, dpc);
XMLHelper xmlHelper = new XMLHelper();
xmlHelper.addTopObject(cDC);
xmlHelper.save_to_xml(cpaFileName);
}
/**
* Enables the generation of the CPX file, which provides the results of AGG in its file format.
*/
public void enableCpxFileGeneration() {
this.generateCpxFile = true;
}
/**
* Sets the options from the henshin interface in the associated part in AGG.
*
* @param epc The container with the rules, options and many more within AGG for calculating the critical pairs.
* @param options The options set for the calculation by the henshin interface.
*/
private void setOptionsOnContainer(ExcludePairContainer epc, CDAOptions options) {
// input values
epc.enableComplete(options.isComplete());
// no more supported, since theses parameters are predefined
epc.enableReduce(options.essentialCP);
// epc.enableConsistent(options.isConsistent());
epc.enableStrongAttrCheck(options.isStrongAttrCheck());
epc.enableEqualVariableNameOfAttrMapping(options.isEqualVName());
epc.enableIgnoreIdenticalRules(options.isIgnoreSameRules());
epc.enableReduceSameMatch(options.isReduceSameRuleAndSameMatch());
epc.enableDirectlyStrictConfluent(options.isDirectlyStrictConfluent());
epc.enableDirectlyStrictConfluentUpToIso(options.isDirectlyStrictConfluentUpToIso());
epc.enablePACs(true);
}
/**
* Sets the creation of rule parameters for all attributes used in a rule. The default case is to create rule
* parameters for all attributes used in a rule.
*
* @param createRuleParameterForAllAttributes The new boolean value for the creation of rule parameters for all
* rules beeing exported to AGG.
*/
public void setCreateRuleParameterForAllAttributes(boolean createRuleParameterForAllAttributes) {
this.createRuleParameterForAllAttributes = createRuleParameterForAllAttributes;
}
private void setAppliedAnalysisToEssential(CPAResult cpaResult) {
for (CriticalPair cp : cpaResult.getCriticalPairs()) {
cp.setAppliedAnalysis(CriticalPair.AppliedAnalysis.ESSENTIAL);
}
}
private void setAppliedAnalysisToNotEssential(CPAResult cpaResult) {
for (CriticalPair cp : cpaResult.getCriticalPairs()) {
cp.setAppliedAnalysis(CriticalPair.AppliedAnalysis.COMPLETE);
}
}
}