blob: 858961c2ac9fd203ab5dbaffab5ebf1f6ef80ec4 [file] [log] [blame]
package org.eclipse.emf.henshin.multicda.cda.framework;
import static org.junit.Assert.assertTrue;
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.TreeSet;
import org.eclipse.emf.henshin.model.Module;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.emf.henshin.model.Unit;
import org.eclipse.emf.henshin.model.resource.HenshinResourceSet;
import org.eclipse.emf.henshin.multicda.cda.ConflictAnalysis;
import org.eclipse.emf.henshin.multicda.cda.DependencyAnalysis;
import org.eclipse.emf.henshin.multicda.cda.MultiGranularAnalysis;
import org.eclipse.emf.henshin.multicda.cda.Utils;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.AtomSize;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.Conditions;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.ConditionsSet;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.MinimalSize;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.Provable;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.ReasonSize;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.StateProvider;
import org.eclipse.emf.henshin.multicda.cda.framework.Condition.State;
import org.eclipse.emf.henshin.multicda.cda.units.Atom;
import org.eclipse.emf.henshin.multicda.cda.units.Reason;
import org.eclipse.emf.henshin.multicda.cda.units.Span;
import org.eclipse.emf.henshin.multicda.cda.units.SymmetricReason;
import org.eclipse.emf.henshin.multicda.cpa.result.CriticalPair;
import org.eclipse.emf.henshin.preprocessing.RulePreparator;
/**
* This worker provides execution, analysis and testing for Multi CDA
* @author Jevgenij Huebert
*/
public class CdaWorker extends Worker {
private MultiGranularAnalysis analyser;
public Set<Rule> first = new HashSet<>();
public Set<Rule> second = new HashSet<>();
private Set<Reason> minimalReasons = new TreeSet<>();
private Set<Reason> reasons = new TreeSet<>();
private Set<Atom> atoms = new HashSet<>();
private int iCheckedCounter = 0;
private Options options;
private boolean reseted = true;
public static Map<String, Set<String>> ignore = new HashMap<>();
private Module module;
public CdaWorker(String henshin, String rule, Options... options) {
this(henshin, rule, rule, options);
}
/**
* Executes all rules containing in the henshin file.
*
* @param henshin
* path to the Henshin file
* @param options
* 1:dependency, 2:prepare, 3:nonDeletionSecondRule, 4:printHeader,
* 5:printResult, 6:silent
*/
public CdaWorker(String henshin, Options... options) {
this(henshin, null, null, options);
}
/**
* Run CDA
*
* @param henshin
* path to the henshin file
* @param firstRule
* name of the first rule
* @param secondRule
* name of the second rule
* @param options
* 1:dependency, 2:prepare, 3:nonDeletionSecondRule, 4:printHeader,
* 5:printResult, 6:silent
*/
public CdaWorker(String henshin, String firstRule, String secondRule, Options... options) {
this.options = new Options();
if (options.length != 0)
this.options = options[0];
if (henshin.isEmpty()
|| ((firstRule != null && !firstRule.isEmpty()) ^ (secondRule != null && !secondRule.isEmpty())))
return;
HenshinResourceSet resourceSet = new HenshinResourceSet(henshin.substring(0, henshin.lastIndexOf("/") + 1));
try {
module = resourceSet.getModule(henshin.substring(henshin.lastIndexOf("/") + 1, henshin.length()), false);
} catch (Exception e) {
System.err.println(e.getMessage());
return;
}
if (firstRule == null) {
firstRule = "All";
for (Unit u : module.getUnits())
if (u instanceof Rule)
first.add((Rule) u);
} else
first.add((Rule) module.getUnit(firstRule));
if (secondRule == null) {
secondRule = "All";
for (Unit u : module.getUnits())
if (u instanceof Rule)
second.add((Rule) u);
} else
second.add((Rule) module.getUnit(secondRule));
if (first == null)
System.err.println("Rule " + firstRule + " not found");
if (second == null)
System.err.println("Rule " + secondRule + " not found");
if (first == null || second == null)
return;
for (Rule r1 : first)
for (Rule r2 : second) {
if (r1 != null && r2 != null
&& (ignore.get(r1.getName()) == null || !ignore.get(r1.getName()).contains(r2.getName()))) {
CdaWorker tester = new CdaWorker(r1, r2, options);
analyser = tester.analyser;
NAME = tester.NAME;
minimalReasons.addAll(tester.getMinimalReasons());
atoms.addAll(tester.getAtoms());
reasons.addAll(tester.getResult());
}
}
reseted = false;
}
/**
* Run CDA
*
* @param first
* rule
* @param second
* rule
* @param options
* 1:dependency, 2:prepare, 3:nonDeletionSecondRule, 4:printHeader,
* 5:printResult, 6:silent
*/
public CdaWorker(Rule first, Rule second, Options... options) {
this.first.add(first);
this.second.add(second);
this.options = new Options();
if (options.length != 0)
this.options = options[0];
init();
}
protected void init() {
reseted = false;
NAME = "CDA Tester";
Rule f = first.iterator().next();
Rule s = second.iterator().next();
assertTrue(print("No first rules are found", true, false), f != null);
assertTrue(print("No second rules are found", true, false), s != null);
if (options.is(Options.PREPARE)) {
if (first != second) {
f = RulePreparator.prepareRule(f);
s = RulePreparator.prepareRule(s);
} else {
f = RulePreparator.prepareRule(f);
s = f;
}
}
if (options.is(Options.DEPENDENCY))
analyser = new DependencyAnalysis(f, s);
else
analyser = new ConflictAnalysis(f, s);
atoms = new HashSet<>(analyser.computeAtoms());
minimalReasons = new HashSet<>(analyser.computeResultsCoarse());
reasons = new HashSet<>(analyser.computeResultsFine());
for (Reason r : reasons)
if (r.is("CFCR")) {
System.out.println(r + "\n" + r.getRule1().getName() + "\n" + r.getRule2().getName());
}
if (options.is(Options.PRINT_HEADER) && (!options.is(Options.PRINT_WHEN_RESULT) || !reasons.isEmpty())) {
System.out.println("\n\t\t " + f.getName() + " --> " + s.getName() + "\n\t\t\tCDA");
print(options.toCDAString() + "\n");
}
if (options.is(Options.PRINT_RESULT) && (!options.is(Options.PRINT_WHEN_RESULT) || !reasons.isEmpty())) {
CdaWorker.print(new TreeSet<>(reasons), false, options.is(Options.PRINT_COMPLETE));
print();
System.out.println();
}
}
public Set<Reason> getMinimalReasons() {
return minimalReasons;
}
/**
* @return
*/
@Override
public Set<Reason> getResult() {
return reasons;
}
/**
* @return
*/
public Set<Atom> getAtoms() {
return atoms;
}
public boolean check(Provable... conditions) {
if (reseted)
return false;
return check(true, false, false, conditions);
}
private boolean check(boolean printError, boolean print, boolean assertError, Provable... conditions) {
boolean result = false;
for (Provable p : conditions) {
result = iChecker(p, printError);
String ruleNames = getRuleNames();
String methodName = Thread.currentThread().getStackTrace()[3].getMethodName();
if (assertError)
assertTrue(methodName + ": " + p + " '" + ruleNames + "' - failed", result);
if (result && print)
System.out.println(methodName + ": " + p + " '" + ruleNames + "' - succeed");
if (p instanceof Conditions && !result && printError)
System.err.println(methodName + ": " + p + " '" + ruleNames + "' - failed");
}
return result;
}
private String getRuleNames() {
String result = "";
for (Rule r : first)
if (r == null)
result += ", first rule not found";
else
result += ", \"" + r.getName() + "\"";
result = result.substring(2) + ", ";
String temp = "";
for (Rule r : second)
if (r == null)
temp += ", second rule not found";
else
temp += ", \"" + r.getName() + "\"";
return result + (temp.isEmpty() ? "" : temp.substring(2));
}
private boolean iChecker(Provable conditions, boolean printError) {
String methodName = Thread.currentThread().getStackTrace()[4].getMethodName();
String ruleNames = getRuleNames();
if (conditions instanceof ReasonSize) {
ReasonSize rs = (ReasonSize) conditions;
boolean failed = false;
if (rs instanceof AtomSize)
failed = !rs.proove(getAtoms().size());
else if (rs instanceof MinimalSize)
failed = !rs.proove(getMinimalReasons().size());
else
failed = !rs.proove(getResult().size());
if (failed) {
if (printError)
System.err.println(
methodName + ": " + conditions + " '" + ruleNames + "' - failed: not " + conditions
+ " but " + getResult().size() + "\n" + print(getResult(), false, true, false));
return false;
} else
return true;
}
if (conditions instanceof ConditionsSet) {
Set<Span> spans = new HashSet<>();
ConditionsSet cs = (ConditionsSet) conditions;
if (cs.atoms)
spans.addAll(atoms);
if (cs.minimals)
spans.addAll(minimalReasons);
if (cs.reasons)
spans.addAll(reasons);
Set<Span> remained = new HashSet<>();
for (Span r : spans)
if (r instanceof Atom && cs.atoms
|| (r instanceof Reason && ((Reason) r).isMinimalReason() && cs.minimals)
|| (r instanceof Reason && !((Reason) r).isMinimalReason() && cs.reasons))
if (!cs.proove(r))
remained.add(r);
if (!cs.getRest().isEmpty()) {
boolean error = false;
for (Provable p : cs.getRest())
if ((p.getState().is(StateProvider.ATOM) && cs.atoms) || (p.getState().is(StateProvider.MINIMAL) && cs.minimals)
|| (!p.getState().is(StateProvider.MINIMAL) && !p.getState().is(StateProvider.ATOM) && cs.reasons)) {
error = true;
if (printError)
System.err.println(methodName + ": " + p + "\n - failed: not found!");
}
return !error;
} else if (printError && !remained.isEmpty()) {
System.err.println(methodName + ": folowing Reasons were not defined in your conditions:\n"
+ print(remained, false, false, false));
}
return true;
} else if (conditions instanceof Conditions)
if (((Conditions) conditions).state.is(State.Atom)) {
for (Atom a : atoms)
if (conditions.proove(a))
return true;
} else if (((Conditions) conditions).state.is(State.Minimal)) {
for (Reason m : minimalReasons)
if (conditions.proove(m))
return true;
} else
for (Reason r : getResult()) {
if (conditions.proove(r))
return true;
}
else if (printError)
System.err.println(methodName + ": Condition '" + conditions + "' was not found in '" + ruleNames + "'");
return false;
}
private Set<? extends Reason> getSymmetricReasons() {
Set<Reason> result = new HashSet<>();
for (Reason r : getResult())
if (r instanceof SymmetricReason)
result.add(r);
return result;
}
private Set<? extends Reason> getAsymmetricReasons() {
Set<Reason> result = new HashSet<>();
for (Reason r : getResult())
if (!(r instanceof SymmetricReason))
result.add(r);
return result;
}
/**
* Resets this tester. After that you can check your conditions again.
*/
public void reset() {
reseted = true;
reasons.clear();
minimalReasons.clear();
atoms.clear();
cps = null;
}
/**
* Prints and returns the printed String of the given reasons
*
* @param reasons
* to print
* @param errorCompleteOut
* describes if the printed String should be an Error and if the
* output String should be printed at all or just returned back.
* default settings are: error = false, out = true
* @return printed String of reasons
*/
public static String print(Set<? extends Span> reasons, boolean... errorCompleteOut) {
List<Span> result = new ArrayList<>();
result.addAll(reasons);
return print(result, errorCompleteOut);
}
public static String print(List<? extends Span> reasons, boolean... errorCompleteOut) {
String result = "";
boolean error = errorCompleteOut.length != 0 && errorCompleteOut[0];
boolean out = errorCompleteOut.length < 3 || errorCompleteOut[2];
for (Span reason : reasons)
result += reason + "\n";
if (out)
if (error)
System.err.println("\n" + result + "\n");
else
System.out.println(result);
return result;
}
/**
* Returns the Rules in a List
*
* @return list with rule1 and rule2
*/
public Set<Rule> getRules() {
Set<Rule> result = new HashSet<>(first);
result.addAll(second);
return result;
}
private Set<CriticalPair> cps = null;
/**
* Compares reasons with given Critical Pairs. If a match was not found it can
* be asserted.
*
* @param cps
* to compare
* @param asserts
* true if an assertion should be done by failed comparison
* @return Map of matched critical Pairs and Reasons
*/
public Map<CriticalPair, Reason> compare(Set<CriticalPair> cps, boolean... assertsPrint) {
this.cps = cps;
comparedResults = Utils.compare(cps, getResult());
if (assertsPrint.length < 2 || assertsPrint[1])
if (comparedResults.size() == getResult().size() && comparedResults.size() == cps.size())
System.out.println("Comparison finished normaly...");
else
System.err.println("Not all CPs are perfectly matched with CRs.\nFound matches: "
+ comparedResults.size() + " of " + getResult().size() + " CRs and " + cps.size() + " CPs");
assertTrue(
"\nNot all CPs are perfectly matched with CRs.\nFound matches: " + comparedResults.size() + " of "
+ getResult().size() + " CRs and " + cps.size() + " CPs",
(assertsPrint.length == 0 || !assertsPrint[0])
|| comparedResults.size() == getResult().size() && comparedResults.size() == cps.size());
return comparedResults;
}
@Override
public void ready() {
int iRest = reasons.size() - iCheckedCounter;
if (iRest > 0)
print("Not all Conflict Reasons are tested. " + iRest + (iRest == 1 ? " is" : " are") + " remaining.");
if (comparedResults != null)
print("Found matches: " + comparedResults.size() + " of " + getResult().size() + " Reasons and "
+ cps.size() + " Critical Pairs",
comparedResults.size() != getResult().size() || comparedResults.size() != cps.size());
super.ready();
reset();
}
@Override
public String toString() {
if (analyser instanceof ConflictAnalysis)
return reasons.size() + (reasons.size() > 1 ? " Conflict Reasons" : " Conflict Reason");
if (analyser instanceof DependencyAnalysis)
return reasons.size() + (reasons.size() > 1 ? " Dependency Reasons" : " Dependency Reason");
else
return super.toString();
}
public Options getOptions() {
return options;
}
public Module getModule() {
return module;
}
}