/** * 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.mmint.mid.operator; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.jdt.annotation.NonNull; import edu.toronto.cs.se.mmint.MMINT; import edu.toronto.cs.se.mmint.MMINTConstants; 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.MIDFactory; import edu.toronto.cs.se.mmint.mid.MIDPackage; import edu.toronto.cs.se.mmint.mid.Model; import edu.toronto.cs.se.mmint.mid.ModelEndpoint; import edu.toronto.cs.se.mmint.mid.diagram.library.MIDDiagramUtils; import edu.toronto.cs.se.mmint.mid.editor.Diagram; import edu.toronto.cs.se.mmint.mid.operator.Operator; import edu.toronto.cs.se.mmint.mid.operator.OperatorInput; import edu.toronto.cs.se.mmint.mid.operator.impl.OperatorImpl; 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.MIDOperatorIOUtils; import edu.toronto.cs.se.mmint.mid.utils.MIDRegistry; public class Map extends OperatorImpl { // input-output private final static @NonNull String IN_MIDS = "mids"; private final static @NonNull String OUT_MIDS = "mappedMids"; private final static @NonNull String GENERIC_OPERATORTYPE = "MAPPER"; // constants private final static @NonNull String MAPPED_MID_SUFFIX = "_map"; private final static @NonNull String MIDREL_MODELTYPE_URI_SUFFIX = "Rel"; private final static @NonNull String MIDOPER_MODELTYPE_URI_SUFFIX = "Oper"; @Override public boolean isAllowedGeneric(GenericEndpoint genericTypeEndpoint, GenericElement genericType, EList<OperatorInput> inputs) throws MMINTException { boolean allowed = super.isAllowedGeneric(genericTypeEndpoint, genericType, inputs); if (!allowed) { return false; } if (genericType.getName().equals("Filter") || genericType.getName().equals("Map") || genericType.getName().equals("Reduce")) { return false; } return true; } @Override public Operator startWorkflowInstance(EList<OperatorInput> inputs, EList<OperatorGeneric> generics, MID workflowMID) throws MMINTException { Operator newOperator = super.startWorkflowInstance(inputs, generics, workflowMID); // create the vararg mapped mids Operator mapperOperatorType = (Operator) generics.get(0).getGeneric(); Model midModelType = MIDTypeRegistry.getMIDModelType(); Model midrelModelType = MIDTypeRegistry.getType(MIDPackage.eNS_URI + MIDREL_MODELTYPE_URI_SUFFIX); for (int i = 0; i < mapperOperatorType.getOutputs().size(); i++) { Model outputModelType = (mapperOperatorType.getOutputs().get(i).getTarget() instanceof ModelRel) ? midrelModelType : midModelType; String outputModelId = MIDRegistry.getNextWorkflowID(workflowMID); Model outputModel = outputModelType.createWorkflowInstance(outputModelId, workflowMID); ModelEndpoint outputModelEndpoint = this.getOutputs().get(0).createWorkflowInstance( outputModel, newOperator, OperatorPackage.eINSTANCE.getOperator_Outputs().getName()); outputModelEndpoint.setName(outputModelEndpoint.getName() + i); } return newOperator; } private Model createOutputMIDModel(String outputName, MID outputMID, Model midModelType, MID instanceMID) throws Exception { String baseOutputPath = (instanceMID == null) ? MMINT.getActiveInstanceMIDFile().getFullPath().toOSString() : MIDRegistry.getModelUri(instanceMID); String outputMIDPath = FileUtils.getUniqueUri( FileUtils.replaceFileNameInUri(baseOutputPath, outputName + MAPPED_MID_SUFFIX), true, false); FileUtils.writeModelFile(outputMID, outputMIDPath, true); Model outputMIDModel = midModelType.createInstanceAndEditor( outputMIDPath, instanceMID); return outputMIDModel; } private Model createOutputMIDRelModel(String outputName, MID outputMID, Model midrelModelType, MID instanceMID, Set<Model> midrelShortcuts) throws Exception { Model outputMIDModel = createOutputMIDModel(outputName, outputMID, midrelModelType, instanceMID); // create gmf shortcuts Diagram outputMIDModelDiagram = (Diagram) outputMIDModel.getEditors().get(0); View gmfDiagramRoot = (View) FileUtils.readModelFile(outputMIDModelDiagram.getUri(), true); for (Model midrelShortcut : midrelShortcuts) { MIDDiagramUtils.createModelShortcut(midrelShortcut, gmfDiagramRoot); } FileUtils.writeModelFile(gmfDiagramRoot, outputMIDModelDiagram.getUri(), true); return outputMIDModel; } private java.util.Map<String, Model> map( @NonNull List<Model> inputMIDModels, @NonNull Operator mapperOperatorType, @NonNull Set<EList<OperatorInput>> mapperInputSet, java.util.Map<String, MID> instanceMIDsByName) throws Exception { // create output MIDs java.util.Map<String, MID> mapperOutputMIDsByName = mapperOperatorType.getOutputs().stream() .collect(Collectors.toMap( outputModelTypeEndpoint -> outputModelTypeEndpoint.getName(), outputModelTypeEndpoint -> MIDFactory.eINSTANCE.createMID())); MID mapperMID = Boolean.parseBoolean( MMINT.getPreference(MMINTConstants.PREFERENCE_MENU_OPERATORS_ENABLED)) ? MIDFactory.eINSTANCE.createMID() : null; // start operator types java.util.Map<String, Set<Model>> midrelShortcutsByOutputName = new HashMap<>(); java.util.Map<String, Set<MID>> midrelMIDsByOutputName = new HashMap<>(); Set<Model> midoperModelShortcuts = new HashSet<>(), midoperModelRelShortcuts = new HashSet<>(); for (EList<OperatorInput> mapperInputs : mapperInputSet) { try { EList<OperatorGeneric> mapperGenerics = mapperOperatorType.selectAllowedGenerics(mapperInputs); java.util.Map<String, Model> mapperOutputsByName = mapperOperatorType.startInstance( mapperInputs, null, mapperGenerics, mapperOutputMIDsByName, mapperMID) .getOutputsByName(); // get gmf shortcuts to create (output MIDRels/MIDOpers need gmf shortcuts to model endpoints) if (mapperMID != null) { for (OperatorInput operatorInput : mapperInputs) { if (operatorInput.getModel() instanceof ModelRel) { midoperModelRelShortcuts.add(operatorInput.getModel()); } else { midoperModelShortcuts.add(operatorInput.getModel()); } } } for (Entry<String, Model> mapperOutput : mapperOutputsByName.entrySet()) { if (mapperMID != null) { if (mapperOutput.getValue() instanceof ModelRel) { midoperModelRelShortcuts.add(mapperOutput.getValue()); } else { midoperModelShortcuts.add(mapperOutput.getValue()); } } if (mapperOutput.getValue() instanceof ModelRel) { Set<Model> midrelShortcutsToAdd = ((ModelRel) mapperOutput.getValue()).getModelEndpoints() .stream() .map(ModelEndpoint::getTarget) .collect(Collectors.toSet()); Set<Model> midrelShortcuts = midrelShortcutsByOutputName.putIfAbsent( mapperOutput.getKey(), midrelShortcutsToAdd); if (midrelShortcuts != null) { midrelShortcuts.addAll(midrelShortcutsToAdd); } Set<MID> midrelMIDsToAdd = ((ModelRel) mapperOutput.getValue()).getModelEndpoints() .stream() .map(modelEndpoint -> modelEndpoint.getTarget().getMIDContainer()) .collect(Collectors.toSet()); Set<MID> midrelMIDs = midrelMIDsByOutputName.putIfAbsent( mapperOutput.getKey(), midrelMIDsToAdd); if (midrelMIDs != null) { midrelMIDs.addAll(midrelMIDsToAdd); } } } } catch (Exception e) { // other than errors, the operator can fail because of input constraints due to the cartesian product MMINTException.print( IStatus.WARNING, "Operator " + mapperOperatorType + " execution error, skipping it", e); } } // store output MIDs Model midModelType = MIDTypeRegistry.getMIDModelType(); Model midrelModelType = MIDTypeRegistry.getType(MIDPackage.eNS_URI + MIDREL_MODELTYPE_URI_SUFFIX); List<Model> outputMIDModels = new ArrayList<>(); // pass 1: no midrels for (Entry<String, MID> outputMIDByName : mapperOutputMIDsByName.entrySet()) { String outputName = outputMIDByName.getKey(); MID outputMID = outputMIDByName.getValue(); boolean isMIDRel = midrelShortcutsByOutputName.get(outputName) != null; if (isMIDRel) { continue; } Model outputMIDModel = createOutputMIDModel(outputName, outputMID, midModelType, instanceMIDsByName.get(outputName)); outputMIDModels.add(outputMIDModel); } // pass 2: midrels only for (Entry<String, MID> outputMIDByName : mapperOutputMIDsByName.entrySet()) { String outputName = outputMIDByName.getKey(); MID outputMID = outputMIDByName.getValue(); boolean isMIDRel = midrelShortcutsByOutputName.get(outputName) != null; if (!isMIDRel) { continue; } MID instanceMID = instanceMIDsByName.get(outputName); Model outputMIDModel = createOutputMIDRelModel(outputName, outputMID, midrelModelType, instanceMID, midrelShortcutsByOutputName.get(outputName)); outputMIDModels.add(outputMIDModel); for (MID midrelMID : midrelMIDsByOutputName.get(outputName)) { String midrelMIDUri = MIDRegistry.getModelUri(midrelMID); if (midrelMID != instanceMID) { // can't create the rel continue; } Model midrelMIDModel = instanceMID.getExtendibleElement(midrelMIDUri); ModelRel midrelRel = MIDTypeHierarchy.getRootModelRelType().createBinaryInstanceAndEndpoints( null, outputMIDModel, midrelMIDModel, instanceMID); midrelRel.setName(midrelMIDModel.getName()); } } //TODO MMINT[OPERATOR] MIDOper's destiny? // create midoper // String baseOutputUri = MIDRegistry.getModelAndModelElementUris(instanceMID, MIDLevel.INSTANCES)[0]; // if (operatorMID != null) { // Model midoperModelType = MultiModelTypeRegistry.getType(MIDPackage.eNS_URI + MIDOPER_MODELTYPE_URI_SUFFIX); // String operatorMIDUri = MultiModelUtils.getUniqueUri( // MultiModelUtils.replaceFileNameInUri(baseOutputUri, mapperOperatorType.getName() + MAPPED_MID_SUFFIX), // true, // false); // MultiModelUtils.createModelFile(operatorMID, operatorMIDUri, true); // Model operatorMIDModel = midoperModelType.createInstanceAndEditor( // operatorMIDUri, // ModelOrigin.CREATED, // instanceMID); // outputsByName.put(OUT_MIDS + i, operatorMIDModel); // // create gmf shortcuts // Diagram operatorMIDModelDiagram = (Diagram) operatorMIDModel.getEditors().get(0); // View gmfDiagramRoot = (View) MultiModelUtils.getModelFile(operatorMIDModelDiagram.getUri(), true); // //TODO MMINT[DIAGRAM] This is wrong, I'd need the supertype // String gmfDiagramPluginId = MultiModelTypeRegistry.getTypeBundle( // operatorMIDModelDiagram.getMetatypeUri()).getSymbolicName(); // MIDDiagramViewProvider gmfViewProvider = new MIDDiagramViewProvider(); // for (Model midoperModelShortcut : midoperModelShortcuts) { // Node gmfNode = gmfViewProvider.createModel_2012( // midoperModelShortcut, // gmfDiagramRoot, // -1, // true, // new PreferencesHint(gmfDiagramPluginId)); // EAnnotation shortcutAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); // shortcutAnnotation.setSource("Shortcut"); // shortcutAnnotation.getDetails().put("modelID", MultiModelEditPart.MODEL_ID); // gmfNode.getEAnnotations().add(shortcutAnnotation); // } // //TODO MMINT[MAP] Fix refresh problems, don't know if there can be shortcuts for gmf links (binary model rels) //// for (Model midoperModelRelShortcut : midoperModelRelShortcuts) { //// View gmfView; //// //TODO MMINT[MAP] Create function to do this //// if (midoperModelRelShortcut instanceof BinaryModelRel) { //// gmfView = gmfViewProvider.createBinaryModelRel_4015( //// midoperModelRelShortcut, //// gmfDiagramRoot, //// -1, //// true, //// new PreferencesHint(gmfDiagramPluginId)); //// } //// else { //// gmfView = gmfViewProvider.createModelRel_2014( //// midoperModelRelShortcut, //// gmfDiagramRoot, //// -1, //// true, //// new PreferencesHint(gmfDiagramPluginId)); //// } //// EAnnotation shortcutAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); //// shortcutAnnotation.setSource("Shortcut"); //// shortcutAnnotation.getDetails().put("modelID", MultiModelEditPart.MODEL_ID); //// gmfView.getEAnnotations().add(shortcutAnnotation); //// } // MultiModelUtils.createModelFile(gmfDiagramRoot, operatorMIDModelDiagram.getUri(), true); // } return MIDOperatorIOUtils.setVarargs(outputMIDModels, OUT_MIDS); } @Override public java.util.Map<String, Model> run( java.util.Map<String, Model> inputsByName, java.util.Map<String, GenericElement> genericsByName, java.util.Map<String, MID> outputMIDsByName) throws Exception { // input List<Model> inputMIDModels = MIDOperatorIOUtils.getVarargs(inputsByName, IN_MIDS); EList<MID> inputMIDs = new BasicEList<>(); for (Model inputMIDModel : inputMIDModels) { inputMIDs.add((MID) inputMIDModel.getEMFInstanceRoot()); } Operator mapperOperatorType = (Operator) genericsByName.get(GENERIC_OPERATORTYPE); java.util.Map<String, MID> instanceMIDsByName = new HashMap<>(); if (outputMIDsByName.containsKey(OUT_MIDS)) { MID instanceMID = outputMIDsByName.get(OUT_MIDS); instanceMIDsByName = mapperOperatorType.getOutputs().stream() .collect(Collectors.toMap( outputModelTypeEndpoint -> outputModelTypeEndpoint.getName(), outputModelTypeEndpoint -> instanceMID)); } else { List<MID> instanceMIDs = MIDOperatorIOUtils.getVarargs(outputMIDsByName, OUT_MIDS); for (int i = 0; i < mapperOperatorType.getOutputs().size(); i++) { instanceMIDsByName.put(mapperOperatorType.getOutputs().get(i).getName(), instanceMIDs.get(i)); } } // find all possible combinations of inputs for operatorType and execute them Set<EList<OperatorInput>> operatorInputSet = mapperOperatorType.findAllowedInputs(inputMIDs); java.util.Map<String, Model> outputsByName = this.map(inputMIDModels, mapperOperatorType, operatorInputSet, instanceMIDsByName); // store model elements created in the input mids for (int i = 0; i < inputMIDModels.size(); i++) { FileUtils.writeModelFile(inputMIDs.get(i), inputMIDModels.get(i).getUri(), true); } return outputsByName; } }