/** * Copyright (c) 2012-2016 Marsha Chechik, Alessio Di Sandro, Michalis Famelis, * Rick Salay, Naama Ben-David. * 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: * Naama Ben-David - Implementation. * Alessio Di Sandro - Generalization to all metamodels. */ package edu.toronto.cs.se.modelepedia.z3.mavo; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import edu.toronto.cs.se.mavo.MAVOCollection; import edu.toronto.cs.se.mavo.MAVODecision; import edu.toronto.cs.se.mavo.MAVOElement; import edu.toronto.cs.se.mavo.MAVORoot; import edu.toronto.cs.se.mavo.MayDecision; import edu.toronto.cs.se.mmint.MMINT; 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.mavo.library.MAVOGMFDiagramUtils; import edu.toronto.cs.se.mmint.mavo.reasoning.IMAVOReasoningEngine.MAVOTruthValue; import edu.toronto.cs.se.mmint.mid.MID; import edu.toronto.cs.se.mmint.mid.MIDLevel; import edu.toronto.cs.se.mmint.mid.Model; import edu.toronto.cs.se.mmint.mid.editor.Diagram; import edu.toronto.cs.se.mmint.mid.relationship.BinaryModelRel; import edu.toronto.cs.se.mmint.mid.relationship.Mapping; import edu.toronto.cs.se.mmint.mid.relationship.MappingReference; import edu.toronto.cs.se.mmint.mid.relationship.ModelElementReference; import edu.toronto.cs.se.mmint.mid.relationship.ModelEndpointReference; import edu.toronto.cs.se.mmint.mid.relationship.ModelRel; import edu.toronto.cs.se.mmint.mid.utils.FileUtils; import edu.toronto.cs.se.mmint.mid.utils.MIDRegistry; import edu.toronto.cs.se.modelepedia.z3.reasoning.Z3ReasoningEngine; public class MAVORefiner { private static final @NonNull String REFINED_MODEL_SUFFIX = "_refined"; private static final @NonNull String MODELRELTYPE_URI = "http://se.cs.toronto.edu/mmint/MAVORefinementRel"; private static final @NonNull String MODELREL_NAME = "refinement"; private static final @NonNull String REFINED_LINK_NAME = "refined"; private static final @NonNull String DELETED_LINK_NAME = "deleted"; private Z3ReasoningEngine reasoner; public MAVORefiner(@NonNull Z3ReasoningEngine reasoner) { this.reasoner = reasoner; } private @NonNull Map<String, MAVOElement> getModelObjectsToRefine(@NonNull MAVORoot rootModelObj, @NonNull MAVORoot refinedRootModelObj, @NonNull String refinedModelUri, @NonNull Map<MAVOElement, MAVOElement> refinementMap) { Map<String, MAVOElement> modelObjsToRefine = new HashMap<String, MAVOElement>(); Resource refinedResource = refinedRootModelObj.eResource(); TreeIterator<EObject> iter = rootModelObj.eAllContents(); while (iter.hasNext()) { EObject modelObj = iter.next(); // skip non-may elements if (!(modelObj instanceof MAVOElement) || !((MAVOElement) modelObj).isMay()) { continue; } String modelObjUri = MIDRegistry.getModelAndModelElementUris(modelObj, MIDLevel.INSTANCES)[1]; String refinedModelObjUri = refinedModelUri + modelObjUri.substring(modelObjUri.lastIndexOf(MMINT.ECORE_MODEL_URI_SEPARATOR)); MAVOElement refinedModelObj; try { refinedModelObj = (MAVOElement) FileUtils.readModelObject(refinedModelObjUri, refinedResource); } catch (Exception e) { MMINTException.print(IStatus.WARNING, "Can't get model object " + refinedModelObjUri + ", skipping it", e); continue; } modelObjsToRefine.put(refinedModelObj.getFormulaVariable(), refinedModelObj); refinementMap.put(refinedModelObj, (MAVOElement) modelObj); } return modelObjsToRefine; } /** * Takes a map of model elements and their new MAVOTruthValues and changes the model accordingly. */ private void refineModel(@NonNull Map<String, MAVOElement> modelObjsToRefine, @NonNull Map<String, MAVOTruthValue> refinedTruthValues, @NonNull Map<MAVOElement, MAVOElement> refinementMap) { //TODO MMINT[MU-MMINT] Generalize for Var for (Entry<String, MAVOTruthValue> entry : refinedTruthValues.entrySet()) { MAVOElement refinedModelObj = modelObjsToRefine.get(entry.getKey()); switch (entry.getValue()) { case TRUE: refinedModelObj.setMay(false); //TODO MMINT[MU-MMINT] Need to detect when a decision is made automatically, e.g. choice of an element has cascading effects to the alternatives Iterator<MAVOCollection> iter = refinedModelObj.getCollections().iterator(); while (iter.hasNext()) { MAVOCollection mavoCollection = iter.next(); if (mavoCollection.eContainer() instanceof MayDecision) { iter.remove(); } } break; case FALSE: EcoreUtil.delete(refinedModelObj, true); refinementMap.put(refinementMap.remove(refinedModelObj), null); break; case MAYBE: default: // no refinement refinementMap.remove(refinedModelObj); } } } private void refineMayDecision(MAVORoot refinedRootModelObj, MAVOCollection mayAlternative) { MayDecision mayDecision = (MayDecision) mayAlternative.eContainer(); String mayDecisionFormulaVar = mayDecision.getFormulaVariable(); for (MAVODecision mayDecisionToDelete : refinedRootModelObj.getDecisions()) { if (!(mayDecisionToDelete instanceof MayDecision)) { continue; } if (mayDecisionFormulaVar.equals(mayDecisionToDelete.getFormulaVariable())) { EcoreUtil.delete(mayDecisionToDelete, true); break; } } } private void populateRefinementRel(@NonNull ModelRel refinementRel, @NonNull Map<MAVOElement, MAVOElement> refinementMap) { ModelEndpointReference modelEndpointRef = refinementRel.getModelEndpointRefs().get(0); ModelEndpointReference refinedModelEndpointRef = refinementRel.getModelEndpointRefs().get(1); // TODO MMINT[MODELREL] It should really be simple to get this with inheritance (MAVORefinementRel has 0 mappings, but the supertype has them) Mapping refinementMappingType = refinementRel.getMetatype().getMappingRefs().get(0).getObject(); for (Entry<MAVOElement, MAVOElement> refinementEntry : refinementMap.entrySet()) { MAVOElement refinedModelObj = refinementEntry.getKey(); MAVOElement modelObj = refinementEntry.getValue(); if (modelObj == null) { modelObj = refinedModelObj; refinedModelObj = null; } boolean isBinary = false; String linkName = DELETED_LINK_NAME; EList<ModelElementReference> modelElemRefs = new BasicEList<>(); try { modelElemRefs.add(modelEndpointRef.createModelElementInstanceAndReference(modelObj, null)); if (refinedModelObj != null) { isBinary = true; linkName = REFINED_LINK_NAME; modelElemRefs.add(refinedModelEndpointRef.createModelElementInstanceAndReference(refinedModelObj, null)); } MappingReference refinementMappingRef = refinementMappingType.createInstanceAndReferenceAndEndpointsAndReferences(isBinary, modelElemRefs); refinementMappingRef.getObject().setName(linkName); } catch (MMINTException e) { MMINTException.print(IStatus.WARNING, "Can't create refinement link", e); } } } private void refineDiagram(org.eclipse.gmf.runtime.notation.Diagram refinedDiagram, @NonNull MAVORoot refinedRootModelObj, Map<MAVOElement, MAVOElement> refinementMap) { Map<String, View> refinedDiagramViews = MAVOGMFDiagramUtils.getDiagramViews(refinedDiagram); // remove views that are no longer in the diagram for (Entry<MAVOElement, MAVOElement> refinementEntry : refinementMap.entrySet()) { if (refinementEntry.getValue() != null) { // need only removal refinements continue; } String formulaVar = refinementEntry.getKey().getFormulaVariable(); View diagramViewToRemove = refinedDiagramViews.get(formulaVar); EcoreUtil.delete(diagramViewToRemove, true); } // assign views to refined model objects TreeIterator<EObject> iter = refinedRootModelObj.eAllContents(); while (iter.hasNext()) { EObject refinedModelObj = iter.next(); //TODO MMINT[MAVO] What if the diagram has some non-mavo elements? if (!(refinedModelObj instanceof MAVOElement)) { continue; } String formulaVar = ((MAVOElement) refinedModelObj).getFormulaVariable(); View diagramView = refinedDiagramViews.get(formulaVar); if (diagramView == null) { // can happen when some diagram views are in their default position continue; } diagramView.setElement(refinedModelObj); } refinedDiagram.setElement(refinedRootModelObj); } public @NonNull Model refine(@NonNull Model model, @Nullable Diagram modelDiagram, @Nullable MAVOCollection mayAlternative, @NonNull String smtEncoding, @Nullable Z3MAVOModelParser z3ModelParser) throws Exception { // create mid artifacts String refinedModelUri = FileUtils.getUniqueUri(FileUtils.addFileNameSuffixInUri(model.getUri(), REFINED_MODEL_SUFFIX), true, false); FileUtils.copyTextFileAndReplaceText(model.getUri(), refinedModelUri, "", "", true); MID instanceMID = model.getMIDContainer(); Model refinedModel = model.getMetatype().createInstance(refinedModelUri, instanceMID); ModelRel modelRelType = MIDTypeRegistry.getType(MODELRELTYPE_URI); if (modelRelType == null) { MMINTException.print(IStatus.WARNING, "Can't find " + MODELRELTYPE_URI + " type, fallback to root ModelRel type", null); modelRelType = MIDTypeHierarchy.getRootModelRelType(); } BinaryModelRel refinementRel = modelRelType.createBinaryInstanceAndEndpoints( null, model, refinedModel, instanceMID); refinementRel.setName(MODELREL_NAME); // refine MAVORoot rootModelObj = (MAVORoot) FileUtils.readModelFile(model.getUri(), true); MAVORoot refinedRootModelObj = (MAVORoot) FileUtils.readModelFile(refinedModelUri, true); Map<MAVOElement, MAVOElement> refinementMap = new HashMap<>(); Map<String, MAVOElement> modelObjsToRefine = getModelObjectsToRefine(rootModelObj, refinedRootModelObj, refinedModelUri, refinementMap); Map<String, MAVOTruthValue> refinedTruthValues = reasoner.mayBackbone(smtEncoding, z3ModelParser, new HashSet<>(modelObjsToRefine.values())); refineModel(modelObjsToRefine, refinedTruthValues, refinementMap); if (mayAlternative != null) { refineMayDecision(refinedRootModelObj, mayAlternative); } populateRefinementRel(refinementRel, refinementMap); // write refinement to file FileUtils.writeModelFile(refinedRootModelObj, refinedModelUri, true); if (modelDiagram != null) { org.eclipse.gmf.runtime.notation.Diagram refinedDiagram = (org.eclipse.gmf.runtime.notation.Diagram) FileUtils.readModelFile(modelDiagram.getUri(), true); refineDiagram(refinedDiagram, refinedRootModelObj, refinementMap); String refinedModelDiagramUri = FileUtils.replaceFileExtensionInUri(refinedModelUri, modelDiagram.getFileExtensions().get(0)); FileUtils.writeModelFile(refinedDiagram, refinedModelDiagramUri, true); FileUtils.openEclipseEditor(refinedModelDiagramUri, modelDiagram.getId(), true); } refinedModel.createInstanceEditor(); return refinedModel; } }