/** * 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.utils; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; 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.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import edu.toronto.cs.se.mmint.MMINT; import edu.toronto.cs.se.mmint.MMINTException; import edu.toronto.cs.se.mmint.mid.EMFInfo; import edu.toronto.cs.se.mmint.mid.ExtendibleElementEndpoint; import edu.toronto.cs.se.mmint.mid.MID; import edu.toronto.cs.se.mmint.mid.MIDFactory; import edu.toronto.cs.se.mmint.mid.MIDLevel; import edu.toronto.cs.se.mmint.mid.Model; import edu.toronto.cs.se.mmint.mid.ModelElement; import edu.toronto.cs.se.mmint.mid.ModelEndpoint; 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.reasoning.MIDConstraintChecker; import edu.toronto.cs.se.mmint.mid.relationship.BinaryModelRel; import edu.toronto.cs.se.mmint.mid.relationship.ExtendibleElementEndpointReference; import edu.toronto.cs.se.mmint.mid.relationship.ExtendibleElementReference; 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.ui.GMFUtils; /** * The registry for querying a multimodel. * * @author Alessio Di Sandro * */ public class MIDRegistry { public final static String RESOURCE_URI_PREFIX = "platform:/resource"; public final static String ECORE_EREFERENCE_URI_PREFIX = "@"; public final static String ECORE_ROOT_FEATURE = "root"; /** * Gets a reference to an extendible element in a list of references to * extendible elements. * * @param elementUri * The uri of the extendible element. * @param elementRefs * The list of references to extendible elements. * @return The reference to the extendible element, null if it can't be * found. */ public static <T extends ExtendibleElementReference> T getReference(String elementUri, EList<T> elementRefs) { if (elementUri == null) { return null; } for (T elementRef : elementRefs) { if (elementUri.equals(elementRef.getUri())) { return elementRef; } } return null; } /** * Gets a reference to an extendible element in a list of references to * extendible elements. * * @param correspondingElementRef * The corresponding reference to extendible element, i.e. a * reference to an extendible element with the same uri of the * one to get. * @param elementRefs * The list of references to extendible elements. * @return The reference to the extendible element, null if it can't be * found. */ public static <T extends ExtendibleElementReference> T getReference(T correspondingElementRef, EList<T> elementRefs) { if (correspondingElementRef == null) { return null; } return getReference(correspondingElementRef.getUri(), elementRefs); } /** * Gets the extendible element endpoints in a list of extendible element * endpoints. * * @param targetUri * The uri of the extendible element which is the target of the * endpoint. * @param endpoints * The list of extendible element endpoints. * @return The extendible element endpoints. */ public static <T extends ExtendibleElementEndpoint> List<T> getEndpoints(String targetUri, EList<T> endpoints) { if (targetUri == null) { return null; } List<T> targetEndpoints = new ArrayList<T>(); for (T endpoint : endpoints) { if (targetUri.equals(endpoint.getTargetUri())) { targetEndpoints.add(endpoint); } } return targetEndpoints; } /** * Gets the references to an extendible element endpoint in a list of * references to extendible element endpoints. * * @param targetUri * The uri of the extendible element which is the target of the * endpoint. * @param endpointRefs * The list of references to extendible element endpoints. * @return The references to the extendible element endpoints. */ public static <T extends ExtendibleElementEndpointReference> List<T> getEndpointReferences(String targetUri, EList<T> endpointRefs) { if (targetUri == null) { return null; } List<T> targetEndpointRefs = new ArrayList<T>(); for (T endpointRef : endpointRefs) { if (targetUri.equals(endpointRef.getTargetUri())) { targetEndpointRefs.add(endpointRef); } } return targetEndpointRefs; } public static void addEndpointCardinality(String uri, Map<String, Integer> cardinalityTable) { Integer value = cardinalityTable.get(uri); Integer newValue = (value == null) ? new Integer(1) : new Integer(value.intValue()+1); cardinalityTable.put(uri, newValue); } public static void subtractEndpointCardinality(String uri, Map<String, Integer> cardinalityTable) throws MMINTException { Integer value = cardinalityTable.get(uri); if (value == null) { throw new MMINTException("Uri " + uri + " doesn't exist in the cardinality table"); } cardinalityTable.put(uri, new Integer(value.intValue()-1)); } public static boolean checkNewEndpointUpperCardinality(ExtendibleElementEndpoint typeEndpoint, Map<String, Integer> cardinalityTable) { int upperBound = typeEndpoint.getUpperBound(); if (upperBound == -1) { return true; } Integer existingEndpoints = cardinalityTable.get(typeEndpoint.getUri()); int numEndpoints = (existingEndpoints == null) ? 0 : existingEndpoints; if (numEndpoints < upperBound) { return true; } return false; } public static String[] getModelAndModelElementUris(EObject modelObj, MIDLevel level) { String modelUri, modelElemUri; String attributeFeatureName = null; if (level == MIDLevel.INSTANCES && modelObj instanceof PrimitiveEObjectWrapper) { attributeFeatureName = ((PrimitiveEObjectWrapper) modelObj).getFeature().getName(); modelObj = ((PrimitiveEObjectWrapper) modelObj).getOwner(); } URI emfUri = EcoreUtil.getURI(modelObj); String uri = emfUri.toString(); if (level == MIDLevel.INSTANCES) { modelElemUri = (uri.startsWith(RESOURCE_URI_PREFIX)) ? uri.substring(RESOURCE_URI_PREFIX.length()) : uri; modelUri = modelElemUri.substring(0, modelElemUri.indexOf(MMINT.ECORE_MODEL_URI_SEPARATOR)); if (attributeFeatureName != null) { modelElemUri += MMINT.URI_SEPARATOR + attributeFeatureName; } } else { modelUri = ((EPackage) EcoreUtil.getRootContainer(modelObj)).getNsURI(); // safe against metamodels in state modelElemUri = modelUri + MMINT.ECORE_MODEL_URI_SEPARATOR + uri.substring(uri.indexOf(MMINT.ECORE_MODEL_URI_SEPARATOR)+MMINT.ECORE_MODEL_URI_SEPARATOR.length()); } return new String[] {modelUri, modelElemUri}; } public static String getModelUri(EObject modelObj) { String modelUri; if (modelObj instanceof EClass) { // == MIDLevel.TYPES modelUri = ((EPackage) EcoreUtil.getRootContainer(modelObj)).getNsURI(); // safe against metamodels in state } else { // == MIDLevel.INSTANCES if (modelObj instanceof PrimitiveEObjectWrapper) { // unwrap modelObj = ((PrimitiveEObjectWrapper) modelObj).getOwner(); } String emfUri = EcoreUtil.getURI(modelObj).toString(); if (emfUri.startsWith(RESOURCE_URI_PREFIX)) { emfUri = emfUri.substring(RESOURCE_URI_PREFIX.length()); } modelUri = emfUri.substring(0, emfUri.indexOf(MMINT.ECORE_MODEL_URI_SEPARATOR)); } return modelUri; } public static String getModelElementUri(EObject modelObj) { String modelElemUri; String emfUri = EcoreUtil.getURI(modelObj).toString(); if (modelObj instanceof EClass) { // == MIDLevel.TYPES String metamodelUri = MIDRegistry.getModelUri(modelObj); modelElemUri = metamodelUri + MMINT.ECORE_MODEL_URI_SEPARATOR + emfUri.substring(emfUri.indexOf(MMINT.ECORE_MODEL_URI_SEPARATOR)+MMINT.ECORE_MODEL_URI_SEPARATOR.length()); } else { // == MIDLevel.INSTANCES String attributeFeatureName = null; if (modelObj instanceof PrimitiveEObjectWrapper) { // unwrap attributeFeatureName = ((PrimitiveEObjectWrapper) modelObj).getFeature().getName(); modelObj = ((PrimitiveEObjectWrapper) modelObj).getOwner(); } if (emfUri.startsWith(RESOURCE_URI_PREFIX)) { emfUri = emfUri.substring(RESOURCE_URI_PREFIX.length()); } modelElemUri = emfUri; if (attributeFeatureName != null) { modelElemUri += MMINT.URI_SEPARATOR + attributeFeatureName; } } return modelElemUri; } //TODO MMINT[MODELELEMENT] some info here are redundant and/or misplaced, review EMFInfo //TODO MMINT[MODELELEMENT] add support for non-containment EReferences public static EMFInfo getModelElementEMFInfo(EObject modelObj, MIDLevel level) { EMFInfo eInfo = MIDFactory.eINSTANCE.createEMFInfo(); String className, featureName = null, relatedClassName = null; boolean isAttribute = false; if (level == MIDLevel.INSTANCES) { if (modelObj instanceof PrimitiveEObjectWrapper) { className = ((PrimitiveEObjectWrapper) modelObj).getOwner().eClass().getName(); featureName = ((PrimitiveEObjectWrapper) modelObj).getFeature().getName(); isAttribute = true; } else { className = modelObj.eClass().getName(); if (modelObj.eContainer() != null) { featureName = modelObj.eContainingFeature().getName(); relatedClassName = modelObj.eContainer().eClass().getName(); } else { featureName = ECORE_ROOT_FEATURE; } } } else { if (modelObj instanceof EStructuralFeature) { className = ((EClass) modelObj.eContainer()).getName(); featureName = ((EStructuralFeature) modelObj).getName(); isAttribute = (modelObj instanceof EAttribute); if (modelObj instanceof EReference) { relatedClassName = ((EReference) modelObj).getEType().getName(); } } else { className = ((EClass) modelObj).getName(); } } eInfo.setClassName(className); eInfo.setFeatureName(featureName); eInfo.setRelatedClassName(relatedClassName); eInfo.setAttribute(isAttribute); return eInfo; } public static String getModelElementName(EMFInfo eInfo, EObject modelObj, MIDLevel level) { String name; if (level == MIDLevel.INSTANCES) { ComposedAdapterFactory adapterFactory = GMFUtils.getAdapterFactory(); AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(adapterFactory); name = eInfo.toInstanceString(); if (modelObj instanceof PrimitiveEObjectWrapper) { name = name.replace(MMINT.MODELELEMENT_PRIMITIVEVALUE_PLACEHOLDER, ((PrimitiveEObjectWrapper) modelObj).getValue().toString()); name = name.replace(MMINT.MODELELEMENT_EMFVALUE_PLACEHOLDER, labelProvider.getText(((PrimitiveEObjectWrapper) modelObj).getOwner())); } else { name = name.replace(MMINT.MODELELEMENT_EMFVALUE_PLACEHOLDER, labelProvider.getText(modelObj)); } } else { name = eInfo.toTypeString(); } return name; } public static ModelElementReference getModelElementReference(ModelEndpointReference modelEndpointRef, EObject modelObj) { ModelElement modelElemType = MIDConstraintChecker.getAllowedModelElementType(modelEndpointRef, modelObj); String modelElemUri = MIDRegistry.getModelAndModelElementUris(modelObj, MIDLevel.INSTANCES)[1] + MMINT.ROLE_SEPARATOR + modelElemType.getUri(); return MIDRegistry.getReference(modelElemUri, modelEndpointRef.getModelElemRefs()); } public static @Nullable Diagram getModelDiagram(@NonNull Model model) { Diagram modelDiagram; try { modelDiagram = (Diagram) model.getEditors().stream() .filter(editor -> editor instanceof Diagram) .findFirst() .get(); } catch (NoSuchElementException e) { MMINTException.print(IStatus.WARNING, "No diagram registered for model " + model.getName() + ", returning null", e); modelDiagram = null; } return modelDiagram; } public static Set<Operator> getInputOperators(Model model, MID mid) { return mid.getOperators().stream() .filter(operator -> operator.getInputs().stream() .anyMatch(input -> input.getTarget() == model)) .collect(Collectors.toSet()); } public static Set<Operator> getOutputOperators(Model model, MID mid) { return mid.getOperators().stream() .filter(operator -> operator.getOutputs().stream() .anyMatch(output -> output.getTarget() == model)) .collect(Collectors.toSet()); } public static Set<Operator> getInputOutputOperators(Model model, MID mid) { Set<Operator> ioOperators = new HashSet<>(); ioOperators.addAll(MIDRegistry.getInputOperators(model, mid)); ioOperators.addAll(MIDRegistry.getOutputOperators(model, mid)); return ioOperators; } public static Set<BinaryModelRel> getConnectedBinaryModelRels(Model model, MID mid) { return mid.getModelRels().stream() .filter(modelRel -> modelRel instanceof BinaryModelRel) .filter(modelRel -> modelRel.getModelEndpoints().stream() .anyMatch(endpoint -> endpoint.getTarget() == model)) .map(modelRel -> (BinaryModelRel) modelRel) .collect(Collectors.toSet()); } public static Set<ModelEndpoint> getConnectedNaryModelRelEndpoints(Model model, MID mid) { return mid.getModelRels().stream() .filter(modelRel -> !(modelRel instanceof BinaryModelRel)) .flatMap(modelRel -> modelRel.getModelEndpoints().stream()) .filter(endpoint -> endpoint.getTarget() == model) .collect(Collectors.toSet()); } public static String getNextWorkflowID(MID workflowMID) { final String WORKFLOW_ID_PREFIX = "w"; TreeSet<String> x = new TreeSet<>(workflowMID.getExtendibleTable().keySet()); String lastID = x.floor(WORKFLOW_ID_PREFIX + "9999"); int numID = (lastID == null || !lastID.startsWith(WORKFLOW_ID_PREFIX)) ? -1 : Integer.parseInt(lastID.substring(1)); String nextID = WORKFLOW_ID_PREFIX + (numID + 1); return nextID; } }