blob: 0fd39a3ccc927da8c0283e137b72a722550b38da [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2013 E.D.Willink 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:
* E.D.Willink - initial API and implementation
* E.D.Willink - Bug 360072
*******************************************************************************/
package org.eclipse.ocl.examples.common.utils;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
//import org.eclipse.ocl.ecore.Constraint;
//import org.eclipse.ocl.ecore.delegate.InvocationBehavior;
//import org.eclipse.ocl.ecore.delegate.SettingBehavior;
import org.eclipse.ocl.examples.common.label.AbstractLabelGenerator;
import org.eclipse.ocl.examples.common.label.ILabelGenerator;
import org.eclipse.ocl.examples.common.label.LabelGeneratorRegistry;
//import org.eclipse.ocl.utilities.UMLReflection;
public class EcoreUtils
{
/**
* The global SIMPLE_NAME_REGISTRY is used by simpleNameFor to generate simple names
* for objects; typically the name property. The SIMPLE_NAME_REGISTRY delegates unsupported
* label generation to the global ILabelGenerator.Registry.INSTANCE.
*/
public static @NonNull ILabelGenerator.Registry SIMPLE_NAME_REGISTRY = new LabelGeneratorRegistry(ILabelGenerator.Registry.INSTANCE);
/**
* The global QUALIFIED_NAME_REGISTRY is used by qualifiedNameFor to generate qualified names
* for objects; typically a :: separted hierarchical name. The QUALIFIED_NAME_REGISTRY delegates unsupported
* label generation to the SIMPLE_NAME_REGISTRY.
*/
public static @NonNull ILabelGenerator.Registry QUALIFIED_NAME_REGISTRY = new LabelGeneratorRegistry(ILabelGenerator.Registry.INSTANCE);
static {
/**
* Install an ENamedElement label generator that suppresses hierarchy.
*/
SIMPLE_NAME_REGISTRY.install(ENamedElement.class, new AbstractLabelGenerator<ENamedElement>(ENamedElement.class)
{
public void buildLabelFor(@NonNull Builder labelBuilder, @NonNull ENamedElement labelledObject) {
String name = labelledObject.getName();
if (name != null)
labelBuilder.appendString(name);
else {
labelBuilder.appendString("<null-named-");
labelBuilder.appendString(labelledObject.getClass().getSimpleName());
labelBuilder.appendString(">");
}
}
});
/**
* Install an ENamedElement label generator that :: separates hierarchical names.
*/
QUALIFIED_NAME_REGISTRY.install(ENamedElement.class, new AbstractLabelGenerator<ENamedElement>(ENamedElement.class)
{
public void buildLabelFor(@NonNull Builder labelBuilder, @NonNull ENamedElement labelledObject) {
String name = labelledObject.getName();
if (name != null)
labelBuilder.appendString(name);
else {
labelBuilder.appendString("<null-named-");
labelBuilder.appendString(labelledObject.getClass().getSimpleName());
labelBuilder.appendString(">");
}
}
});
}
public static <E extends EObject> E copy(E newObject) {
return EcoreUtil.copy(newObject);
}
/**
* Convert the map return from EcoreUtil.UnresolvedProxyCrossReferencer.find(xx)
* into a textual diagnosis.
* <br>
* Returns null if there are no unresolved URIs.
* <br>
* Returns a String containing a title line containing the contextURI and
* subsequent lines identifying each distinct unresolved URI.
*/
public static @Nullable String diagnoseUnresolvedProxies(@NonNull URI contextURI, @NonNull Map<EObject, Collection<EStructuralFeature.Setting>> map) {
if (map.isEmpty())
return null;
Map<String, Map.Entry<EObject, Collection<EStructuralFeature.Setting>>> unresolvedURIs = new HashMap<String, Map.Entry<EObject, Collection<EStructuralFeature.Setting>>>(map.size());
for (Map.Entry<EObject, Collection<EStructuralFeature.Setting>> entry : map.entrySet()) {
EObject key = entry.getKey();
URI uri = EcoreUtil.getURI(key);
if (uri != null) {
String uriString = uri.toString();
if (!unresolvedURIs.containsKey(uriString))
unresolvedURIs.put(uriString, entry);
}
}
StringBuilder s = new StringBuilder();
s.append("Unresolved URIs in '" + String.valueOf(contextURI) + "' :");
for (Map.Entry<String, Map.Entry<EObject, Collection<EStructuralFeature.Setting>>> unresolvedURI : unresolvedURIs.entrySet())
s.append("\n '" + unresolvedURI.getKey() + "'");
return s.toString();
}
public static @NonNull String formatMultiplicity(@Nullable ETypedElement typedElement) {
if (typedElement == null)
return "";
int lower = typedElement.getLowerBound();
int upper = typedElement.getUpperBound();
if (lower == upper)
return "" + Integer.toString(lower);
else if (lower == 0) {
if (upper < 0)
return "*";
else if (upper == 1)
return "?";
}
else if (lower == 1) {
if (upper < 0)
return "+";
}
return Integer.toString(lower) + ".." + (upper >= 0 ? Integer.toString(upper) : "*");
}
public static @NonNull String formatOrdered(@Nullable ETypedElement typedElement) {
boolean isOrdered = typedElement != null ? (typedElement.isOrdered() && typedElement.isMany()) : false;
return isOrdered ? "{ordered}" : "";
}
public static@NonNull String formatString(@Nullable String name) {
return name != null ? name : "<null>";
}
public static @NonNull String formatUnique(@Nullable ETypedElement typedElement) {
boolean isOrdered = typedElement != null ? (typedElement.isUnique() && typedElement.isMany()) : false;
return isOrdered ? "{unique}" : "";
}
public static @Nullable <T extends Adapter> T getAdapter(@Nullable Notifier notifier, Class<T> adapterClass) {
if (notifier == null)
return null;
return getAdapter(notifier.eAdapters(), adapterClass);
}
@SuppressWarnings("unchecked")
public static <T extends Adapter> T getAdapter(List<Adapter> eAdapters, Class<T> adapterClass) {
return (T) EcoreUtil.getAdapter(eAdapters, adapterClass);
}
/**
* Return the specialised value of feature.getEType() resolving any type parameters
* from the specialised type of the sourceObject of the feature.
*
* @param sourceObject
* @param feature
*/
public static EClassifier getEType(EObject sourceObject, @NonNull EStructuralFeature feature) {
EGenericType targetGenericType = feature.getEGenericType();
ETypeParameter targetTypeParameter = targetGenericType.getETypeParameter();
if ((targetTypeParameter != null) && (sourceObject != null)) {
EClass sourceGenericType = feature.getEContainingClass();
EObject typeParameterContainer = targetTypeParameter.eContainer();
EClass sourceClass = sourceObject.eClass();
EList<EGenericType> allSourceGenericSuperTypes = sourceClass.getEAllGenericSuperTypes();
for (EGenericType sourceGenericSuperType : allSourceGenericSuperTypes) {
if (sourceGenericSuperType.getERawType() == typeParameterContainer) {
EList<EGenericType> sourceTypeArguments = sourceGenericSuperType.getETypeArguments();
int i = sourceGenericType.getETypeParameters().indexOf(targetTypeParameter);
if ((0 <= i) && (i < sourceTypeArguments.size())) {
EGenericType sourceTypeArgument = sourceTypeArguments.get(i);
return sourceTypeArgument.getERawType();
}
}
}
}
return targetGenericType.getERawType();
}
/**
* Return the Ecore EStringToStringMapEntry that realises a given Constraint.
*
* @param constraint the constraint
* @return the annotation detail entry, null if not found
*
public static Map.Entry<String, String> getEAnnotationDetail(Constraint constraint) {
List<EModelElement> constrainedElements = constraint.getConstrainedElements();
if (constrainedElements.isEmpty()) {
return null;
}
EModelElement constrainedElement = constrainedElements.get(0);
String keyName;
String stereotype = constraint.getStereotype();
if (UMLReflection.INVARIANT.equals(stereotype)) {
keyName = constraint.getName();
if (constrainedElement instanceof EClass) {
EOperation apiOperation = EcoreUtils.getEcoreInvariant((EClass) constrainedElement, keyName);
if (apiOperation != null) {
constrainedElement = apiOperation;
keyName = InvocationBehavior.BODY_CONSTRAINT_KEY;
}
}
}
else if (UMLReflection.DERIVATION.equals(stereotype)){
keyName = SettingBehavior.DERIVATION_CONSTRAINT_KEY;
}
else if (UMLReflection.INITIAL.equals(stereotype)){
keyName = SettingBehavior.INITIAL_CONSTRAINT_KEY;
}
else if (UMLReflection.BODY.equals(stereotype)){
keyName = InvocationBehavior.BODY_CONSTRAINT_KEY;
}
// else if (UMLReflection.PRECONDITION.equals(stereotype)){
// node = InvocationBehavior.PRECONDITION_CONSTRAINT_KEY;
// }
// else if (UMLReflection.POSTCONDITION.equals(stereotype)){
// node = InvocationBehavior.POSTCONDITION_CONSTRAINT_KEY;
// }
else {
keyName = null;
}
EAnnotation eAnnotation = OCLCommon.getDelegateAnnotation(constrainedElement);
if (eAnnotation == null) {
return null;
}
EMap<String, String> details = eAnnotation.getDetails();
if (keyName != null) {
int indexOfKey = details.indexOfKey(keyName);
if (indexOfKey >= 0) {
return details.get(indexOfKey);
}
}
return null;
} */
/**
* Return the EOperation that realises the name invariant for eClass.
* @param eClass with invariant
* @param name of invariant
* @return the EOperation or null
*/
public static @Nullable EOperation getEcoreInvariant(@NonNull EClass eClass, @NonNull String name) {
for (EOperation eOperation : eClass.getEOperations()) {
if (ClassUtils.equals(name, eOperation.getName()) && EcoreUtil.isInvariant(eOperation)) {
return eOperation;
}
}
return null;
}
public static <T> int getFeatureID(@NonNull Notification notification, @Nullable T expectedNotifier, @NonNull Class<T> featureClass) {
if (expectedNotifier == null)
return Notification.NO_FEATURE_ID;
Object notifier = notification.getNotifier();
if (notifier != expectedNotifier)
return Notification.NO_FEATURE_ID;
T castNotifier = ClassUtils.asClassOrNull(notifier, featureClass);
if (castNotifier == null)
throw new IllegalArgumentException("EcoreUtils.getFeatureID: " + featureClass.getName() + " for a " + notifier.getClass().getName());
return notification.getFeatureID(featureClass);
}
public static @Nullable <T extends ENamedElement> T getNamedElement(@Nullable Collection<T> elements, String name) {
if (elements == null)
return null;
for (T element : elements)
if (ClassUtils.equals(name, element.getName()))
return element;
return null;
}
@SuppressWarnings("unchecked")
public static @Nullable <T extends ENamedElement, R extends T> R getNamedElement(@Nullable Collection<T> elements, @Nullable String name, @Nullable Class<R> returnClass) {
if (elements == null)
return null;
if (name == null)
return null;
if (returnClass == null)
return null;
for (T element : elements)
if (returnClass.isAssignableFrom(element.getClass()) && ClassUtils.equals(name, element.getName()))
return (R) element;
return null;
}
/**
* Return a qualified name for object using the label generators registered
* in the QUALIFIED_NAME_REGISTRY.
*
* @param object to be named
* @return qualified name
*/
public static @NonNull String qualifiedNameFor(@Nullable Object object) {
if (object == null) {
return "<<null>>";
}
Map<ILabelGenerator.Option<?>, Object> options = new HashMap<ILabelGenerator.Option<?>, Object>();
options.put(ILabelGenerator.Builder.SHOW_QUALIFIER, "::");
return QUALIFIED_NAME_REGISTRY.labelFor(object, options);
}
/**
* Return a simple name for object using the label generators registered
* in the SIMPLE_NAME_REGISTRY.
*
* @param object to be named
* @return simple name
*/
public static @NonNull String simpleNameFor(@Nullable Object object) {
if (object == null) {
return "<<null>>";
}
return SIMPLE_NAME_REGISTRY.labelFor(object);
}
}