/** * Copyright (c) 2012-2016 Marsha Chechik, Alessio Di Sandro, Michalis Famelis, * Rick Salay. * 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: * Alessio Di Sandro - Implementation. */ package edu.toronto.cs.se.modelepedia.z3.operator.henshin; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.henshin.interpreter.EGraph; import org.eclipse.emf.henshin.interpreter.Engine; import org.eclipse.emf.henshin.interpreter.Match; import org.eclipse.emf.henshin.interpreter.RuleApplication; import org.eclipse.emf.henshin.interpreter.impl.EGraphImpl; import org.eclipse.emf.henshin.interpreter.impl.EngineImpl; import org.eclipse.emf.henshin.interpreter.impl.RuleApplicationImpl; import org.eclipse.emf.henshin.interpreter.util.InterpreterUtil; import org.eclipse.emf.henshin.model.Module; import org.eclipse.emf.henshin.model.Node; import org.eclipse.emf.henshin.model.Rule; import org.eclipse.emf.henshin.model.resource.HenshinResourceSet; import org.eclipse.emf.henshin.trace.Trace; import edu.toronto.cs.se.mavo.MAVOElement; import edu.toronto.cs.se.mmint.MMINTException; import edu.toronto.cs.se.mmint.MIDTypeHierarchy; import edu.toronto.cs.se.mmint.MIDTypeRegistry; import edu.toronto.cs.se.mmint.mid.GenericElement; import edu.toronto.cs.se.mmint.mid.MID; import edu.toronto.cs.se.mmint.mid.Model; import edu.toronto.cs.se.mmint.mid.relationship.BinaryModelRel; import edu.toronto.cs.se.mmint.mid.utils.FileUtils; import edu.toronto.cs.se.mmint.mid.utils.MIDOperatorIOUtils; import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver; import edu.toronto.cs.se.modelepedia.z3.Z3Utils; /** * Implementation of the algorithm in Fig. 6 of the paper: * * Rick Salay, Michalis Famelis, Julia Rubin, Alessio Di Sandro, Marsha Chechik: * Lifting model transformations to product lines. ICSE 2014: 117-128 * * @author Alessio Di Sandro */ public class ProductLineHenshinTransformation extends LiftingHenshinTransformation { protected static final String SMTLIB_APPLICABILITY_PREAMBLE = "(declare-fun fN (Int) Bool) (declare-fun fC (Int) Bool) (declare-fun fD (Int) Bool) (declare-fun fY (Int) Bool) (assert (forall ((i Int)) (= (fY i) (and (not (fN i)) (fC i) (fD i))))) (declare-fun fX (Int) Bool) (assert (forall ((i Int)) (= (fX i)"; protected static final String SMTLIB_APPLICABILITY_POSTAMBLE = ")))"; /** * Updates the presence condition of the (A)dded model object to be phi * apply at step ruleApplicationsLifting+1 (algorithm line 6). * * @param modelObjA * The (A)dded model object. */ @Override protected void transformModelObjAWhenLifted(MAVOElement modelObjA) { modelObjA.setFormulaVariable(SMTLIB_APPLICABILITY_FUN_APPLY + (ruleApplicationsLifting+1) + Z3Utils.SMTLIB_PREDICATE_END); } @Override protected void createZ3ApplyFormula() { createZ3ApplyFormulaMatchSetNIteration(); createZ3ApplyFormulaMatchSetIteration(modelObjsC, SMTLIB_APPLICABILITY_FUN_C, Z3Utils.SMTLIB_AND, Z3Utils.SMTLIB_TRUE); createZ3ApplyFormulaMatchSetIteration(modelObjsD, SMTLIB_APPLICABILITY_FUN_D, Z3Utils.SMTLIB_AND, Z3Utils.SMTLIB_TRUE); } protected void updateLiterals() { int countLiterals = 0; for (MAVOElement modelObjCDN : modelObjsCDN) { Integer modelObjCDNLiterals = modelObjsLiterals.get(modelObjCDN); if (modelObjCDNLiterals == null) { modelObjCDNLiterals = (modelObjCDN.getFormulaVariable().equals(Z3Utils.SMTLIB_TRUE)) ? new Integer(0) : new Integer(1); modelObjsLiterals.put(modelObjCDN, modelObjCDNLiterals); } countLiterals += modelObjCDNLiterals; } for (MAVOElement modelObjA : modelObjsA) { modelObjsLiterals.put(modelObjA, new Integer(countLiterals)); } } protected void getMatchedModelObjs(Match match, Set<Node> nodes, Set<MAVOElement> modelObjs, Set<MAVOElement> allModelObjs) { for (Node node : nodes) { EObject nodeTarget = match.getNodeTarget(node); if (nodeTarget instanceof MAVOElement) { allModelObjs.add((MAVOElement) nodeTarget); if (((MAVOElement) nodeTarget).getFormulaVariable() != null) { modelObjs.add((MAVOElement) nodeTarget); } } } } private TransformationApplicabilityCondition checkApplicabilityConditions(Rule rule, Engine engine, EGraph graph, Z3IncrementalSolver z3IncSolver) { for (int i = 0; i < rule.getLhs().getNACs().size(); i++) { // one Nac at a time Rule ruleCopyN = EcoreUtil.copy(rule); Set<Node> nodesN = new LinkedHashSet<Node>(), nodesC = new LinkedHashSet<Node>(), nodesD = new LinkedHashSet<Node>(); List<Match> matchesN = findNMatches(ruleCopyN, engine, graph, i, nodesC, nodesD, nodesN); for (int j = 0; j < matchesN.size(); j++) { modelObjsNBar.clear(); modelObjsC.clear(); modelObjsD.clear(); modelObjsCDN.clear(); Match matchNj = matchesN.get(j); addNBarModelObjs(matchNj, nodesN); getMatchedModelObjs(matchNj, nodesC, modelObjsC, modelObjsCDN); getMatchedModelObjs(matchNj, nodesD, modelObjsD, modelObjsCDN); for (int k = 0; k < matchesN.size(); k++) { // same Nac for NBar if (j == k) { continue; } Match matchNk = matchesN.get(k); if (!areMatchesOverlapping(matchNj, matchNk, nodesC, nodesC, nodesD, nodesD)) { continue; } addNBarModelObjs(matchNk, nodesN); } for (int l = 0; l < rule.getLhs().getNACs().size(); l++) { // other Nacs for NBar if (l == i) { continue; } Set<Node> nodesNl = new LinkedHashSet<Node>(), nodesCl = new LinkedHashSet<Node>(), nodesDl = new LinkedHashSet<Node>(); List<Match> matchesNl = findNMatches(EcoreUtil.copy(rule), engine, graph, l, nodesCl, nodesDl, nodesNl); for (int m = 0; m < matchesNl.size(); m++) { Match matchNm = matchesNl.get(m); if (!areMatchesOverlapping(matchNj, matchNm, nodesC, nodesCl, nodesD, nodesDl)) { continue; } addNBarModelObjs(matchNm, nodesNl); } } // check apply formula if (checkZ3ApplicabilityFormula(z3IncSolver, smtEncoding.length())) { return new TransformationApplicabilityCondition(ruleCopyN, matchNj, true); // <NBar,C,D> lifted match } } } // no Nac matched Rule ruleCopy = EcoreUtil.copy(rule); Set<Node> nodesC = new HashSet<Node>(), nodesD = new HashSet<Node>(); getCDNodes(ruleCopy, nodesC, nodesD); List<Match> matches = InterpreterUtil.findAllMatches(engine, ruleCopy, graph, null); for (int i = 0; i < matches.size(); i++) { modelObjsNBar.clear(); modelObjsC.clear(); modelObjsD.clear(); modelObjsCDN.clear(); Match match = matches.get(i); getMatchedModelObjs(match, nodesC, modelObjsC, modelObjsCDN); getMatchedModelObjs(match, nodesD, modelObjsD, modelObjsCDN); // check apply formula if (checkZ3ApplicabilityFormula(z3IncSolver, smtEncoding.length())) { return new TransformationApplicabilityCondition(ruleCopy, match, true); // <C,D> lifted match } } return null; // no matches } @Override protected int matchAndTransformLifting(Rule rule, Engine engine, EGraph graph, Z3IncrementalSolver z3IncSolver, int checkpointA) { RuleApplication application = new RuleApplicationImpl(engine); TransformationApplicabilityCondition condition; // check applicability condition involves defining fN,fC,fD at step ruleApplicationsLifting + // invoking the solver (algorithm line 3) while ((condition = checkApplicabilityConditions(rule, engine, graph, z3IncSolver)) != null) { application.setRule(condition.getMatchedRule()); application.setEGraph(graph); // transform and detect (A)dded model objects modelObjsA.clear(); transformLifting(application, condition.getMatch(), condition.isLiftedMatch()); if (condition.isLiftedMatch()) { ruleApplicationsLifting++; updateChains(); updateLiterals(); } else { ruleApplicationsNotLifting++; } } return smtEncoding.length(); } @Override public Map<String, Model> run( Map<String, Model> inputsByName, Map<String, GenericElement> genericsByName, Map<String, MID> outputMIDsByName) throws Exception { // Using MAVO elements in this operator is a "trick" to reuse the // formulaVariable field as the container of presence conditions. It works // as long as all elements in the Henshin rules are MAVO elements, the only // exception being the root which is always present. // input Model origModel = inputsByName.get(IN_MODEL); super.init(); // function declarations at step 0 for fN-fC-fD (phis) + // function definition at every step for fY (phi apply, algorithm line 2) + // function definition at every step for fX (phi P, external constraint) super.initSMTEncoding(SMTLIB_APPLICABILITY_PREAMBLE, SMTLIB_APPLICABILITY_POSTAMBLE); // do transformations String fullUri = FileUtils.prependWorkspacePathToUri(FileUtils.replaceLastSegmentInUri(origModel.getUri(), "")); HenshinResourceSet hResourceSet = new HenshinResourceSet(fullUri); Module hModule = hResourceSet.getModule(transformationModule, false); Engine hEngine = new EngineImpl(); hEngine.getOptions().put(Engine.OPTION_SORT_VARIABLES, false); EGraph hGraph = new EGraphImpl(hResourceSet.getResource(FileUtils.getLastSegmentFromUri(origModel.getUri()))); if (timeClassicalEnabled) { doTransformationClassical(hModule, hEngine, hGraph); hResourceSet = new HenshinResourceSet(fullUri); hModule = hResourceSet.getModule(transformationModule, false); hEngine = new EngineImpl(); hEngine.getOptions().put(Engine.OPTION_SORT_VARIABLES, false); hGraph = new EGraphImpl(hResourceSet.getResource(FileUtils.getLastSegmentFromUri(origModel.getUri()))); } doTransformationLifting(hModule, hEngine, hGraph); if (transformedConstraintEnabled) { transformedConstraint = smtEncoding.toString(); } // output EObject transformedRootModelObj = null; for (EObject hRoot : hGraph.getRoots()) { if (hRoot instanceof Trace) { continue; } transformedRootModelObj = hRoot; } if (transformedRootModelObj == null) { throw new MMINTException("Can't retrieve transformed root model object"); } Model transformedModelType = MIDTypeRegistry.getType( transformedRootModelObj.eClass().getEPackage().getNsURI()); String transformedMIDModelUri = FileUtils.getUniqueUri( FileUtils.replaceFileExtensionInUri( FileUtils.addFileNameSuffixInUri(origModel.getUri(), TRANSFORMED_MODEL_SUFFIX), transformedModelType.getFileExtension()), true, false); FileUtils.writeModelFile(transformedRootModelObj, transformedMIDModelUri, true); Model transformedModel = transformedModelType.createInstanceAndEditor( transformedMIDModelUri, outputMIDsByName.get(OUT_MODEL)); BinaryModelRel traceRel = MIDTypeHierarchy.getRootModelRelType().createBinaryInstanceAndEndpoints( null, origModel, transformedModel, outputMIDsByName.get(OUT_MODELREL)); traceRel.setName(OUT_MODELREL); Map<String, Model> outputsByName = new HashMap<>(); outputsByName.put(OUT_MODEL, transformedModel); outputsByName.put(OUT_MODELREL, traceRel); Properties outputProperties = new Properties(); writeProperties(outputProperties); MIDOperatorIOUtils.writePropertiesFile( outputProperties, this, origModel, getInputSubdir(), MIDOperatorIOUtils.OUTPUT_PROPERTIES_SUFFIX ); return outputsByName; } }