blob: 6b704057e54be762849896634902a4adc2b83bd7 [file] [log] [blame]
/*
* Copyright (c) 2009-2013, 2015, 2016, 2018, 2019 Eike Stepper (Loehne, Germany) 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:
* Eike Stepper - initial API and implementation
* Victor Roldan Betancort - maintenance
* Simon McDuff - maintenance
* Christian W. Damus (CEA) - support registered dynamic UML profiles
* Christian W. Damus (CEA) - don't process EAnnotations for proxy resolution
*/
package org.eclipse.emf.cdo.common.model;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry;
import org.eclipse.net4j.util.Predicate;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notifier;
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.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
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.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.EPackageImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Various static helper methods for dealing with EMF meta models.
*
* @author Eike Stepper
* @since 2.0
*/
public final class EMFUtil
{
/**
* @since 3.0
*/
public static final String CDO_ANNOTATION_SOURCE = "http://www.eclipse.org/emf/CDO";
/**
* @since 3.0
*/
public static final String CDO_ANNOTATION_KEY_PERSISTENT = "persistent";
/**
* @since 4.2
*/
public static final EReference EOPERATION_EEXCEPTIONS = EcorePackage.eINSTANCE.getEOperation_EExceptions();
/**
* @since 4.2
*/
public static final EReference ETYPED_ELEMENT_ETYPE = EcorePackage.eINSTANCE.getETypedElement_EType();
/**
* @since 4.2
*/
public static final EReference ECLASS_ESUPER_TYPES = EcorePackage.eINSTANCE.getEClass_ESuperTypes();
/**
* @since 4.2
*/
public static final EAttribute ECLASSIFIER_INSTANCE_CLASS_NAME = EcorePackage.eINSTANCE.getEClassifier_InstanceClassName();
/**
* @since 4.2
*/
public static final EReference EOPERATION_EGENERIC_EXCEPTIONS = EcorePackage.eINSTANCE.getEOperation_EGenericExceptions();
/**
* @since 4.2
*/
public static final EReference ETYPED_ELEMENT_EGENERIC_TYPE = EcorePackage.eINSTANCE.getETypedElement_EGenericType();
/**
* @since 4.2
*/
public static final EReference ECLASS_EGENERIC_SUPER_TYPES = EcorePackage.eINSTANCE.getEClass_EGenericSuperTypes();
/**
* @since 4.2
*/
public static final EAttribute ECLASSIFIER_INSTANCE_TYPE_NAME = EcorePackage.eINSTANCE.getEClassifier_InstanceTypeName();
/**
* @since 4.2
*/
public static final Predicate<EStructuralFeature> ATTRIBUTES = new Predicate<EStructuralFeature>()
{
public boolean apply(EStructuralFeature feature)
{
return feature instanceof EAttribute;
}
};
/**
* @since 4.2
*/
public static final Predicate<EStructuralFeature> REFERENCES = new Predicate<EStructuralFeature>()
{
public boolean apply(EStructuralFeature feature)
{
return feature instanceof EReference;
}
};
/**
* @since 4.2
*/
public static final Predicate<EStructuralFeature> CONTAINER_REFERENCES = new Predicate<EStructuralFeature>()
{
public boolean apply(EStructuralFeature feature)
{
if (feature instanceof EReference)
{
EReference reference = (EReference)feature;
return reference.isContainer();
}
return false;
}
};
/**
* @since 4.2
*/
public static final Predicate<EStructuralFeature> CROSS_REFERENCES = new Predicate<EStructuralFeature>()
{
public boolean apply(EStructuralFeature feature)
{
if (feature instanceof EReference)
{
EReference reference = (EReference)feature;
return !(reference.isContainer() || reference.isContainment());
}
return false;
}
};
/**
* @since 4.2
*/
public static final Predicate<EStructuralFeature> CONTAINMENT_REFERENCES = new Predicate<EStructuralFeature>()
{
public boolean apply(EStructuralFeature feature)
{
if (feature instanceof EReference)
{
EReference reference = (EReference)feature;
return reference.isContainment();
}
return false;
}
};
private EMFUtil()
{
}
/**
* @since 4.2
*/
public static URI getPositionalURI(InternalEObject internalEObject)
{
List<String> uriFragmentPath = new ArrayList<String>();
Resource resource;
for (InternalEObject container = internalEObject.eInternalContainer(); (resource = internalEObject.eDirectResource()) == null
&& container != null; container = internalEObject.eInternalContainer())
{
String segment = getPositionalURIFragmentSegment(container, internalEObject.eContainingFeature(), internalEObject);
uriFragmentPath.add(segment);
internalEObject = container;
}
StringBuilder builder = new StringBuilder("/");
builder.append(resource.getContents().indexOf(internalEObject));
for (int i = uriFragmentPath.size() - 1; i >= 0; --i)
{
builder.append('/');
builder.append(uriFragmentPath.get(i));
}
return resource.getURI().appendFragment(builder.toString());
}
/**
* @since 4.2
*/
private static String getPositionalURIFragmentSegment(EObject container, EStructuralFeature eStructuralFeature, InternalEObject eObject)
{
StringBuilder builder = new StringBuilder();
builder.append('@');
builder.append(eStructuralFeature.getName());
if (eStructuralFeature instanceof EAttribute)
{
FeatureMap featureMap = (FeatureMap)container.eGet(eStructuralFeature, false);
for (int i = 0, size = featureMap.size(); i < size; ++i)
{
if (featureMap.getValue(i) == eObject)
{
EStructuralFeature entryFeature = featureMap.getEStructuralFeature(i);
if (entryFeature instanceof EReference && ((EReference)entryFeature).isContainment())
{
builder.append('.');
builder.append(i);
return builder.toString();
}
}
}
builder.append(".-1");
}
else if (eStructuralFeature.isMany())
{
EList<?> eList = (EList<?>)container.eGet(eStructuralFeature, false);
int index = eList.indexOf(eObject);
builder.append('.');
builder.append(index);
}
return builder.toString();
}
public static EPackage getGeneratedEPackage(EPackage ePackage)
{
String packageURI = ePackage.getNsURI();
if (packageURI.equals(EcorePackage.eINSTANCE.getNsURI()))
{
return EcorePackage.eINSTANCE;
}
EPackage.Registry registry = EPackage.Registry.INSTANCE;
return registry.getEPackage(packageURI);
}
public static Map.Entry<String, Object>[] getSortedRegistryEntries(EPackage.Registry packageRegistry)
{
Set<Map.Entry<String, Object>> entries = packageRegistry.entrySet();
@SuppressWarnings("unchecked")
Map.Entry<String, Object>[] array = entries.toArray(new Entry[entries.size()]);
Arrays.sort(array, new Comparator<Map.Entry<String, Object>>()
{
public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2)
{
return o1.getKey().compareTo(o2.getKey());
}
});
return array;
}
/**
* @since 4.6
*/
public static EList<EAnnotation> getAnnotations(EClass eClass, String sourceURI)
{
EList<EAnnotation> annotations = new BasicEList<EAnnotation>();
getAnnotations(eClass, sourceURI, annotations, new HashSet<EClass>());
return annotations;
}
private static void getAnnotations(EClass eClass, String sourceURI, EList<EAnnotation> annotations, Set<EClass> visited)
{
if (visited.add(eClass))
{
for (EAnnotation annotation : eClass.getEAnnotations())
{
if (sourceURI == null || sourceURI.equals(annotation.getSource()))
{
annotations.add(annotation);
}
}
for (EClass superType : eClass.getESuperTypes())
{
getAnnotations(superType, sourceURI, annotations, visited);
}
}
}
public static EPackage getTopLevelPackage(EPackage ePackage)
{
EPackage superPackage = ePackage.getESuperPackage();
return superPackage == null ? ePackage : getTopLevelPackage(superPackage);
}
/**
* @since 2.0
*/
public static EPackage createEPackage(String name, String nsPrefix, String nsURI)
{
EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage();
ePackage.setName(name);
ePackage.setNsPrefix(nsPrefix);
ePackage.setNsURI(nsURI);
return ePackage;
}
/**
* @since 2.0
*/
public static EClass createEClass(EPackage ePackage, String name, boolean isAbstract, boolean isInterface)
{
EClass eClass = EcoreFactory.eINSTANCE.createEClass();
eClass.setName(name);
eClass.setAbstract(isAbstract);
eClass.setInterface(isInterface);
ePackage.getEClassifiers().add(eClass);
return eClass;
}
/**
* @since 2.0
*/
public static EAttribute createEAttribute(EClass eClass, String name, EClassifier type)
{
EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute();
eAttribute.setName(name);
eAttribute.setEType(type);
eClass.getEStructuralFeatures().add(eAttribute);
return eAttribute;
}
/**
* @since 2.0
*/
public static EReference createEReference(EClass eClass, String name, EClassifier type, boolean isRequired, boolean isMany)
{
EReference eReference = EcoreFactory.eINSTANCE.createEReference();
eReference.setName(name);
eReference.setEType(type);
eReference.setLowerBound(isRequired ? 1 : 0);
eReference.setUpperBound(isMany ? -1 : 0);
eClass.getEStructuralFeatures().add(eReference);
return eReference;
}
/**
* @since 4.2
*/
public static EClass[] getConcreteClasses(EPackage ePackage)
{
List<EClass> result = new ArrayList<EClass>(0);
for (EClassifier classifier : ePackage.getEClassifiers())
{
if (classifier instanceof EClass)
{
EClass eClass = (EClass)classifier;
if (!eClass.isAbstract() && !eClass.isInterface())
{
result.add(eClass);
}
}
}
return result.toArray(new EClass[result.size()]);
}
public static EClass[] getPersistentClasses(EPackage ePackage)
{
List<EClass> result = new ArrayList<EClass>();
for (EClassifier classifier : ePackage.getEClassifiers())
{
if (classifier instanceof EClass)
{
result.add((EClass)classifier);
}
}
return result.toArray(new EClass[result.size()]);
}
/**
* @since 3.0
* @deprecated This method is expensive and will be removed in the future.
* @see #isPersistent(EStructuralFeature)
*/
@Deprecated
public static List<EStructuralFeature> getPersistentFeatures(EList<EStructuralFeature> eFeatures)
{
List<EStructuralFeature> result = new ArrayList<EStructuralFeature>();
for (EStructuralFeature feature : eFeatures)
{
if (isPersistent(feature))
{
result.add(feature);
}
}
return result;
}
/**
* Returns <code>true</code> if CDO considers the given feature <i>persistent</i>, <code>false</code> otherwise.
* <p>
* Note that CDO <i>persistent</i> is not identical to {@link EStructuralFeature#isTransient() non-transient} because that can be
* overridden with {@link #CDO_ANNOTATION_KEY_PERSISTENT}. Another reason for possible deviations is that CDO considers transient
* {@link EReference references} <i>persistent</i> if they have a <i>persistent</i> {@link EReference#getEOpposite() opposite}.
* <p>
* Note also that the checks for the aforementioned deviations from {@link EStructuralFeature#isTransient()} make this method somewhat
* expensive. Whenever possible {@link CDOClassInfo#isPersistent(int) CDOClassInfo.isPersistent()} should be called instead.
*
* @since 3.0
*/
public static boolean isPersistent(EStructuralFeature feature)
{
if (feature == ECLASS_ESUPER_TYPES || feature == ETYPED_ELEMENT_ETYPE || feature == EOPERATION_EEXCEPTIONS || feature == ECLASSIFIER_INSTANCE_CLASS_NAME)
{
// http://www.eclipse.org/newsportal/article.php?id=26780&group=eclipse.tools.emf#26780
return false;
}
String persistent = EcoreUtil.getAnnotation(feature, CDO_ANNOTATION_SOURCE, CDO_ANNOTATION_KEY_PERSISTENT);
if (persistent != null)
{
return "true".equalsIgnoreCase(persistent);
}
if (feature.isTransient())
{
// Bug 333950: Transient eRefs with a persistent eOpposite, must be considered persistent
if (feature instanceof EReference)
{
EReference eOpposite = ((EReference)feature).getEOpposite();
if (eOpposite != null && !eOpposite.isTransient())
{
return true;
}
}
return false;
}
return true;
}
public static boolean isDynamicEPackage(Object value)
{
return value.getClass() == EPackageImpl.class;
}
public static String getParentURI(EPackage ePackage)
{
EPackage superPackage = ePackage.getESuperPackage();
String parentURI = superPackage == null ? null : superPackage.getNsURI();
return parentURI;
}
public static void registerPackage(EPackage ePackage, EPackage.Registry... packageRegistries)
{
ePackage.getClass(); // Initialize package in standalone mode
if (packageRegistries == null || packageRegistries.length == 0)
{
EPackage.Registry[] globalRegistry = { EPackage.Registry.INSTANCE };
packageRegistries = globalRegistry;
}
for (EPackage.Registry packageRegistry : packageRegistries)
{
packageRegistry.put(ePackage.getNsURI(), ePackage);
}
}
public static byte[] getEPackageBytes(EPackage ePackage, boolean zipped, EPackage.Registry packageRegistry)
{
try
{
// The package may be nested in other content (e.g., a UML Profile).
// Or, maybe it is just a dynamic package that was not loaded from a resource
Resource resource = ((InternalEObject)ePackage).eDirectResource();
if (resource == null)
{
ResourceSet resourceSet = newEcoreResourceSet(packageRegistry);
resource = resourceSet.createResource(URI.createURI(ePackage.getNsURI()));
// If the package is nested in some container, then copy it into the temporary
// resource so that we don't send content that the server doesn't need
resource.getContents().add(ePackage.eContainer() == null ? ePackage : EcoreUtil.copy(ePackage));
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resource.save(baos, createResourceOptions(zipped));
return baos.toByteArray();
}
catch (Exception ex)
{
throw WrappedException.wrap(ex);
}
}
/**
* @since 3.0
*/
public static EPackage createEPackage(String uri, byte[] bytes, boolean zipped, ResourceSet resourceSet, boolean lookForResource)
{
try
{
Resource resource = null;
if (lookForResource)
{
resource = resourceSet.getResource(URI.createURI(uri), true);
}
if (resource == null)
{
resource = resourceSet.createResource(URI.createURI(uri));
}
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
resource.load(bais, createResourceOptions(zipped));
EList<EObject> contents = resource.getContents();
return (EPackage)contents.get(0);
}
catch (Exception ex)
{
throw WrappedException.wrap(ex);
}
}
private static Map<String, Object> createResourceOptions(boolean zipped)
{
Map<String, Object> options = new HashMap<String, Object>();
if (zipped)
{
options.put(Resource.OPTION_ZIP, true);
}
return options;
}
/**
* @since 4.2
*/
public static <T> T getAdapter(Notifier notifier, Class<T> type)
{
@SuppressWarnings("unchecked")
T adapter = (T)EcoreUtil.getAdapter(notifier.eAdapters(), type);
return adapter;
}
public static void addAdapter(Notifier notifier, Adapter adapter)
{
synchronized (notifier)
{
EList<Adapter> adapters = notifier.eAdapters();
if (!adapters.contains(adapter))
{
adapters.add(adapter);
}
}
}
public static EPackage[] getAllPackages(EPackage ePackage)
{
List<EPackage> result = new ArrayList<EPackage>();
getAllPackages(ePackage, result);
return result.toArray(new EPackage[result.size()]);
}
private static void getAllPackages(EPackage ePackage, List<EPackage> result)
{
result.add(ePackage);
for (EPackage subPackage : ePackage.getESubpackages())
{
getAllPackages(subPackage, result);
}
}
public static String getQualifiedName(EPackage ePackage, String separator)
{
StringBuilder builder = new StringBuilder();
EPackage eSuperPackage = ePackage.getESuperPackage();
if (eSuperPackage != null)
{
builder.append(getQualifiedName(eSuperPackage, separator));
builder.append(separator);
}
builder.append(ePackage.getName());
return builder.toString();
}
public static String getQualifiedName(EClassifier classifier, String separator)
{
StringBuilder builder = new StringBuilder();
EPackage ePackage = classifier.getEPackage();
if (ePackage != null)
{
builder.append(getQualifiedName(ePackage, separator));
builder.append(separator);
}
builder.append(classifier.getName());
return builder.toString();
}
public static ResourceSet newResourceSet(Resource.Factory resourceFactory)
{
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("*", resourceFactory); //$NON-NLS-1$
return resourceSet;
}
public static ResourceSet newEcoreResourceSet(EPackage.Registry packageRegistry)
{
ResourceSet resourceSet = newResourceSet(new EcoreResourceFactoryImpl());
resourceSet.setPackageRegistry(packageRegistry);
return resourceSet;
}
public static ResourceSet newEcoreResourceSet()
{
return newEcoreResourceSet(EPackage.Registry.INSTANCE);
}
/**
* @since 3.0
*/
public static EObject safeResolve(EObject proxy, ResourceSet resourceSet)
{
if (!proxy.eIsProxy())
{
return proxy;
}
EObject resolved = EcoreUtil.resolve(proxy, resourceSet);
if (resolved == proxy)
{
throw new IllegalStateException("Unresolvable proxy: " + ((InternalEObject)proxy).eProxyURI());
}
return resolved;
}
/**
* @since 3.0
*/
public static void safeResolveAll(ResourceSet resourceSet)
{
TreeIterator<Notifier> it = resourceSet.getAllContents();
while (it.hasNext())
{
Notifier notifier = it.next();
if (notifier instanceof EObject)
{
safeResolve((EObject)notifier, resourceSet);
if (notifier instanceof EAnnotation)
{
// we don't need to validate the structure of annotations. The applications that
// define annotations will have to take what they can get
it.prune();
}
else
{
Iterator<EObject> it2 = ((EObject)notifier).eCrossReferences().iterator();
while (it2.hasNext())
{
safeResolve(it2.next(), resourceSet);
}
}
}
}
}
/**
* @see ExtResourceSet
* @since 4.0
*/
public static ExtResourceSet createExtResourceSet(InternalCDOPackageRegistry packageRegistry, boolean delegating, boolean demandLoading)
{
Resource.Factory resourceFactory = new EcoreResourceFactoryImpl();
ExtResourceSet resourceSet = new ExtResourceSet(delegating, demandLoading);
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("*", resourceFactory); //$NON-NLS-1$
resourceSet.setPackageRegistry(packageRegistry);
return resourceSet;
}
/**
* An extension of {@link ResourceSetImpl} that allows demandLoading of resources and delegation of resource lookups,
* to be switched on/off as desired.
*
* @since 4.0
*/
public static class ExtResourceSet extends ResourceSetImpl
{
private boolean delegating;
private boolean demandLoading;
/**
* @since 4.7
*/
public ExtResourceSet(boolean delegating, boolean demandLoading)
{
this.delegating = delegating;
this.demandLoading = demandLoading;
}
public boolean isDelegating()
{
return delegating;
}
public void setDelegating(boolean delegating)
{
this.delegating = delegating;
}
public boolean isDemandLoading()
{
return demandLoading;
}
public void setDemandLoading(boolean demandLoading)
{
this.demandLoading = demandLoading;
}
@Override
protected void demandLoad(Resource resource) throws IOException
{
if (demandLoading)
{
super.demandLoad(resource);
}
}
@Override
protected Resource delegatedGetResource(URI uri, boolean loadOnDemand)
{
if (delegating)
{
return super.delegatedGetResource(uri, loadOnDemand);
}
return null;
}
}
}