/******************************************************************************* * Copyright (c) 2013, 2014 Obeo and others. * 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: * Obeo - initial API and implementation * Philip Langer - adds helpers for opaque elements and opaque element changes *******************************************************************************/ package org.eclipse.emf.compare.uml2.internal.postprocessor.util; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.emf.compare.AttributeChange; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Extension; import org.eclipse.uml2.uml.OpaqueAction; import org.eclipse.uml2.uml.OpaqueBehavior; import org.eclipse.uml2.uml.OpaqueExpression; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.util.UMLUtil; /** * Some utility methods that may be tweaked to allow EMFCompare to scale. * * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a> */ public final class UMLCompareUtil { /** Constructor. */ private UMLCompareUtil() { } /** * Retrieves the base element for the specified stereotype application, i.e. the element to which the * stereotype is applied. * <p> * It first calls {@link UMLUtil#getBaseElement(EObject)}. If it returns null, it then tries to find a * {@link EStructuralFeature} with a name starting with {@link Extension#METACLASS_ROLE_PREFIX}. It * <em>does not</em> verify if the the given {@code stereotypeApplication}'s eClass is defined as a * Stereotype within a Profile because it may lead to load the resource of the Profile. * * @param stereotypeApplication * The stereotype application. * @return The base element. */ public static Element getBaseElement(EObject stereotypeApplication) { if (stereotypeApplication == null) { return null; } Element baseElement = UMLUtil.getBaseElement(stereotypeApplication); final Iterator<EStructuralFeature> features = stereotypeApplication.eClass() .getEAllStructuralFeatures().iterator(); while (features.hasNext() && baseElement == null) { final EStructuralFeature feature = features.next(); if (feature.getName().startsWith(Extension.METACLASS_ROLE_PREFIX)) { final Object value = stereotypeApplication.eGet(feature); if (value instanceof Element) { baseElement = (Element)value; } } } return baseElement; } /** * From the given EReference, it returns the list of EReference which are superset and non union. * * @param reference * The EReference subset from which is requested the non union superset. * @return The list of EReference non union superset. */ public static Iterable<EReference> getNonUnionSupersetReferences(EReference reference) { return Iterables.filter(getSupersetReferences(reference), isNonUnionReference()); } private static Predicate<? super EReference> isNonUnionReference() { return new Predicate<EReference>() { public boolean apply(EReference input) { return input != null && !Iterables.any(input.getEAnnotations(), UMLUtilForCompare.isUnionAnnotation()); } }; } /** * From the given EReference, it returns the list of EReference which are superset. * * @param reference * The EReference subset from which is requested the superset. * @return The list of EReference superset. */ private static Iterable<EReference> getSupersetReferences(EReference reference) { EAnnotation subsetsAnnotation = Iterables.find(reference.getEAnnotations(), UMLUtilForCompare.isSubsetsAnnotation(), null); if (subsetsAnnotation != null) { return Iterables.filter(subsetsAnnotation.getReferences(), EReference.class); } return Collections.emptyList(); } /** * This extends UMLUtil to get the name of the used annotations for subsets and unions. * * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a> */ private static class UMLUtilForCompare extends UMLUtil { public static Predicate<? super EAnnotation> isSubsetsAnnotation() { return new Predicate<EAnnotation>() { public boolean apply(EAnnotation input) { return input != null && input.getSource().equals(ANNOTATION__SUBSETS); } }; } public static Predicate<? super EAnnotation> isUnionAnnotation() { return new Predicate<EAnnotation>() { public boolean apply(EAnnotation input) { return input != null && input.getSource().equals(ANNOTATION__UNION); } }; } } /** * Returns the bodies of the given {@code eObject}, which must be either an OpaqueAction, OpaqueBehavior, * or an OpaqueExpression. * * @throws IllegalArgumentException * If {@code eObject} is not a OpaqueAction, OpaqueBehavior, or an OpaqueExpression. * @param eObject * The OpaqueAction, OpaqueBehavior, or an OpaqueExpression to get the bodies of. * @return The bodies of {@code eObject}. */ public static List<String> getOpaqueElementBodies(EObject eObject) { final List<String> bodies; if (eObject instanceof OpaqueAction) { bodies = ((OpaqueAction)eObject).getBodies(); } else if (eObject instanceof OpaqueBehavior) { bodies = ((OpaqueBehavior)eObject).getBodies(); } else if (eObject instanceof OpaqueExpression) { bodies = ((OpaqueExpression)eObject).getBodies(); } else { throw new IllegalArgumentException(eObject.eClass().getName() + " has no bodies. Only OpaqueAction, OpaqueBehavior, and OpaqueExpression do."); //$NON-NLS-1$ } return bodies; } /** * Returns the languages of the given {@code eObject}, which must be either an OpaqueAction, * OpaqueBehavior, or an OpaqueExpression. * * @throws IllegalArgumentException * If {@code eObject} is not a OpaqueAction, OpaqueBehavior, or an OpaqueExpression. * @param eObject * The OpaqueAction, OpaqueBehavior, or an OpaqueExpression to get the languages of. * @return The bodies of {@code eObject}. */ public static List<String> getOpaqueElementLanguages(EObject eObject) { final List<String> languages; if (eObject instanceof OpaqueAction) { languages = ((OpaqueAction)eObject).getLanguages(); } else if (eObject instanceof OpaqueBehavior) { languages = ((OpaqueBehavior)eObject).getLanguages(); } else if (eObject instanceof OpaqueExpression) { languages = ((OpaqueExpression)eObject).getLanguages(); } else { throw new IllegalArgumentException(eObject.eClass().getName() + " has no languages. Only OpaqueAction, OpaqueBehavior, and OpaqueExpression do."); //$NON-NLS-1$ } return languages; } /** * Returns the body for the given {@code language} of the given {@code eObject}, which must be either an * OpaqueAction, OpaqueBehavior, or an OpaqueExpression. * * @throws IllegalArgumentException * If {@code eObject} is not a OpaqueAction, OpaqueBehavior, or an OpaqueExpression. * @param eObject * The OpaqueAction, OpaqueBehavior, or an OpaqueExpression to get the body of. * @param language * The language for which the body is requested. * @return The body for the given {@code language} of the given {@code eObject}. */ public static String getOpaqueElementBody(EObject eObject, String language) { final List<String> languages = getOpaqueElementLanguages(eObject); final List<String> bodies = getOpaqueElementBodies(eObject); final int index = languages.indexOf(language); return bodies.get(index); } /** * Specifies whether the given {@code diff} is a change of the body attribute of {@link OpaqueAction}, * {@link OpaqueBehavior}, or {@link OpaqueExpression}. * * @param diff * The difference to check. * @return <code>true</code> if it is a change of the body attribute, <code>false</code> otherwise. */ public static boolean isChangeOfOpaqueElementBodyAttribute(Diff diff) { if (diff instanceof AttributeChange) { final EAttribute attribute = ((AttributeChange)diff).getAttribute(); return UMLPackage.eINSTANCE.getOpaqueAction_Body().equals(attribute) || UMLPackage.eINSTANCE.getOpaqueBehavior_Body().equals(attribute) || UMLPackage.eINSTANCE.getOpaqueExpression_Body().equals(attribute); } else { return false; } } /** * Specifies whether the given {@code diff} is a change of the language attribute of {@link OpaqueAction}, * {@link OpaqueBehavior}, or {@link OpaqueExpression}. * * @param diff * The difference to check. * @return <code>true</code> if it is a change of the language attribute, <code>false</code> otherwise. */ public static boolean isChangeOfOpaqueElementLanguageAttribute(Diff diff) { if (diff instanceof AttributeChange) { final EAttribute attribute = ((AttributeChange)diff).getAttribute(); return UMLPackage.eINSTANCE.getOpaqueAction_Language().equals(attribute) || UMLPackage.eINSTANCE.getOpaqueBehavior_Language().equals(attribute) || UMLPackage.eINSTANCE.getOpaqueExpression_Language().equals(attribute); } else { return false; } } }