blob: 5d3a523ddf6e74af18979b8344990bed802d768c [file] [log] [blame]
/*
* Copyright (c) 2008-2014, 2016, 2018-2020 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
* Stefan Winkler - Bug 332912 - Caching subtype-relationships in the CDOPackageRegistry
* Erdal Karaca - added support for HASHMAP CDO Type
* Christian W. Damus (CEA) - don't validate cross-references in EAnnotations
*/
package org.eclipse.emf.cdo.common.model;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.internal.common.messages.Messages;
import org.eclipse.emf.cdo.internal.common.model.CDOClassInfoImpl;
import org.eclipse.emf.cdo.internal.common.model.CDOPackageInfoImpl;
import org.eclipse.emf.cdo.internal.common.model.CDOPackageRegistryImpl;
import org.eclipse.emf.cdo.internal.common.model.CDOPackageUnitImpl;
import org.eclipse.emf.cdo.internal.common.model.CDOTypeImpl;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
import org.eclipse.net4j.util.io.ExtendedDataInput;
import org.eclipse.net4j.util.io.ExtendedDataOutput;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Various static helper methods for dealing with CDO meta models.
*
* @author Eike Stepper
* @since 2.0
*/
public final class CDOModelUtil implements CDOModelConstants
{
private static final EClass ROOT_TYPE = EcorePackage.eINSTANCE.getEObject();
private static CDOType[] coreTypes;
static
{
List<CDOType> types = new ArrayList<>();
registerCoreType(types, EcorePackage.eINSTANCE.getEBigDecimal(), CDOType.BIG_DECIMAL);
registerCoreType(types, EcorePackage.eINSTANCE.getEBigInteger(), CDOType.BIG_INTEGER);
registerCoreType(types, EcorePackage.eINSTANCE.getEBooleanObject(), CDOType.BOOLEAN_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEBoolean(), CDOType.BOOLEAN);
registerCoreType(types, EcorePackage.eINSTANCE.getEByteArray(), CDOType.BYTE_ARRAY);
registerCoreType(types, EcorePackage.eINSTANCE.getEByteObject(), CDOType.BYTE_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEByte(), CDOType.BYTE);
registerCoreType(types, EcorePackage.eINSTANCE.getECharacterObject(), CDOType.CHARACTER_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEChar(), CDOType.CHAR);
registerCoreType(types, EcorePackage.eINSTANCE.getEDate(), CDOType.DATE);
registerCoreType(types, EcorePackage.eINSTANCE.getEDoubleObject(), CDOType.DOUBLE_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEDouble(), CDOType.DOUBLE);
registerCoreType(types, EcorePackage.eINSTANCE.getEFloatObject(), CDOType.FLOAT_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEFloat(), CDOType.FLOAT);
registerCoreType(types, EcorePackage.eINSTANCE.getEIntegerObject(), CDOType.INTEGER_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEInt(), CDOType.INT);
registerCoreType(types, EcorePackage.eINSTANCE.getEJavaClass(), CDOType.JAVA_CLASS);
registerCoreType(types, EcorePackage.eINSTANCE.getEJavaObject(), CDOType.JAVA_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getELongObject(), CDOType.LONG_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getELong(), CDOType.LONG);
registerCoreType(types, EcorePackage.eINSTANCE.getEShortObject(), CDOType.SHORT_OBJECT);
registerCoreType(types, EcorePackage.eINSTANCE.getEShort(), CDOType.SHORT);
registerCoreType(types, EcorePackage.eINSTANCE.getEString(), CDOType.STRING);
coreTypes = types.toArray(new CDOType[types.size()]);
}
private static void registerCoreType(List<CDOType> types, EClassifier classifier, CDOType type)
{
int index = classifier.getClassifierID();
while (index >= types.size())
{
types.add(null);
}
types.set(index, type);
}
private CDOModelUtil()
{
}
/**
* @since 2.0
*/
public static boolean isCorePackage(EPackage ePackage)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[0] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[0] == ePackage;
}
String nsURI = ePackage.getNsURI();
return CORE_PACKAGE_URI.equals(nsURI);
}
/**
* @since 2.0
*/
public static boolean isRoot(EClass eClass)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[1] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[1] == eClass;
}
if (isCorePackage(eClass.getEPackage()))
{
String name = eClass.getName();
return ROOT_CLASS_NAME.equals(name);
}
return false;
}
/**
* @since 2.0
*/
public static boolean isResourcePackage(EPackage ePackage)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[2] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[2] == ePackage;
}
String nsURI = ePackage.getNsURI();
return RESOURCE_PACKAGE_URI.equals(nsURI);
}
/**
* @since 2.0
*/
public static boolean isResource(EClass eClass)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[3] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[3] == eClass;
}
if (isResourcePackage(eClass.getEPackage()))
{
String name = eClass.getName();
return RESOURCE_CLASS_NAME.equals(name);
}
return false;
}
/**
* @since 2.0
*/
public static boolean isResourceFolder(EClass eClass)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[4] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[4] == eClass;
}
if (isResourcePackage(eClass.getEPackage()))
{
String name = eClass.getName();
return RESOURCE_FOLDER_CLASS_NAME.equals(name);
}
return false;
}
/**
* @since 2.0
*/
public static boolean isResourceNode(EClass eClass)
{
return isResourcePackage(eClass.getEPackage());
}
/**
* @since 4.3
*/
public static boolean isResourcePathFeature(EStructuralFeature eStructuralFeature)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[8] == eStructuralFeature)
{
return true;
}
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[9] == eStructuralFeature)
{
return true;
}
if (isResourceNode(eStructuralFeature.eClass()))
{
String name = eStructuralFeature.getName();
if (RESOURCE_NODE_FOLDER_REFERENCE.equals(name) || RESOURCE_NODE_NAME_ATTRIBUTE.equals(name))
{
return true;
}
}
return false;
}
/**
* @since 4.0
*/
public static boolean isTypesPackage(EPackage ePackage)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[5] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[5] == ePackage;
}
String nsURI = ePackage.getNsURI();
return TYPES_PACKAGE_URI.equals(nsURI);
}
/**
* @since 2.0
*/
public static boolean isSystemPackage(EPackage ePackage)
{
return isCorePackage(ePackage) || isResourcePackage(ePackage) || isTypesPackage(ePackage);
}
/**
* @since 4.7
*/
public static boolean isSystemPackageURI(String nsURI)
{
return CORE_PACKAGE_URI.equals(nsURI) || RESOURCE_PACKAGE_URI.equals(nsURI) || TYPES_PACKAGE_URI.equals(nsURI);
}
/**
* @since 4.0
*/
public static boolean isLob(EClassifier eClassifier)
{
if (CDOPackageRegistryImpl.SYSTEM_ELEMENTS[6] != null && CDOPackageRegistryImpl.SYSTEM_ELEMENTS[7] != null)
{
return CDOPackageRegistryImpl.SYSTEM_ELEMENTS[6] == eClassifier || CDOPackageRegistryImpl.SYSTEM_ELEMENTS[7] == eClassifier;
}
if (isTypesPackage(eClassifier.getEPackage()))
{
String name = eClassifier.getName();
return BLOB_CLASS_NAME.equals(name) || CLOB_CLASS_NAME.equals(name);
}
return false;
}
/**
* @since 2.0
*/
public static CDOType getType(EStructuralFeature feature)
{
return getType(feature.getEType());
}
/**
* @since 4.0
*/
public static CDOType getType(byte typeID)
{
return CDOTypeImpl.getType(typeID);
}
/**
* @since 2.0
*/
public static CDOType getType(EClassifier classifier)
{
if (classifier instanceof EClass)
{
return CDOType.OBJECT;
}
if (classifier instanceof EEnum)
{
return CDOType.ENUM_ORDINAL;
}
EDataType eDataType = (EDataType)classifier;
EPackage ePackage = eDataType.getEPackage();
if (isCorePackage(ePackage))
{
CDOType type = getCoreType(eDataType);
if (type != null)
{
return type;
}
}
else if (isTypesPackage(ePackage))
{
String name = eDataType.getName();
if (BLOB_CLASS_NAME.equals(name))
{
return CDOType.BLOB;
}
if (CLOB_CLASS_NAME.equals(name))
{
return CDOType.CLOB;
}
}
return CDOType.CUSTOM;
}
/**
* Core types includes also complex data like EAnnotation, and EEnum
*
* @since 2.0
*/
public static CDOType getCoreType(EClassifier eDataType)
{
int index = eDataType.getClassifierID();
if (0 <= index && index < coreTypes.length)
{
return coreTypes[index];
}
return null;
}
/**
* @since 2.0
*/
public static CDOType getPrimitiveType(Class<? extends Object> primitiveType)
{
if (primitiveType == String.class)
{
return CDOType.STRING;
}
if (primitiveType == Boolean.class)
{
return CDOType.BOOLEAN;
}
if (primitiveType == Integer.class)
{
return CDOType.INT;
}
if (primitiveType == Double.class)
{
return CDOType.DOUBLE;
}
if (primitiveType == Float.class)
{
return CDOType.FLOAT;
}
if (primitiveType == Long.class)
{
return CDOType.LONG;
}
if (primitiveType == Date.class)
{
return CDOType.DATE;
}
if (primitiveType == Byte.class)
{
return CDOType.BYTE;
}
if (primitiveType == Character.class)
{
return CDOType.CHAR;
}
if (primitiveType == Object[].class)
{
return CDOType.OBJECT_ARRAY;
}
if (EEnumLiteral.class.isAssignableFrom(primitiveType) || Enumerator.class.isAssignableFrom(primitiveType))
{
return CDOType.ENUM_LITERAL;
}
if (Map.class.isAssignableFrom(primitiveType))
{
return CDOType.MAP;
}
if (Set.class.isAssignableFrom(primitiveType))
{
return CDOType.SET;
}
if (List.class.isAssignableFrom(primitiveType))
{
return CDOType.LIST;
}
throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOModelUtil.7"), primitiveType)); //$NON-NLS-1$
}
/**
* @since 4.0
*/
public static CDOType getTypeOfObject(Object object)
{
if (object == null || object instanceof CDOID || object instanceof CDORevision)
{
return CDOType.OBJECT;
}
Class<? extends Object> objectClass = object.getClass();
if (objectClass == String.class)
{
return CDOType.STRING;
}
if (objectClass == Integer.class)
{
return CDOType.INTEGER_OBJECT;
}
if (objectClass == Long.class)
{
return CDOType.LONG_OBJECT;
}
if (objectClass == Boolean.class)
{
return CDOType.BOOLEAN_OBJECT;
}
if (objectClass == Character.class)
{
return CDOType.CHARACTER_OBJECT;
}
if (objectClass == Double.class)
{
return CDOType.DOUBLE_OBJECT;
}
if (objectClass == Float.class)
{
return CDOType.FLOAT_OBJECT;
}
if (objectClass == Short.class)
{
return CDOType.SHORT_OBJECT;
}
if (objectClass == Byte.class)
{
return CDOType.BYTE_OBJECT;
}
if (objectClass == byte[].class)
{
return CDOType.BYTE_ARRAY;
}
if (objectClass == Date.class || object instanceof Date)
{
return CDOType.DATE;
}
if (object instanceof EEnumLiteral)
{
return CDOType.ENUM_LITERAL;
}
if (objectClass == BigDecimal.class || object instanceof BigDecimal)
{
return CDOType.BIG_DECIMAL;
}
if (objectClass == BigInteger.class || object instanceof BigInteger)
{
return CDOType.BIG_INTEGER;
}
if (object instanceof Throwable)
{
return CDOType.EXCEPTION;
}
throw new IllegalArgumentException("Object type " + objectClass.getName() + " is not supported.");
}
/**
* @since 2.0
*/
public static CDOPackageInfo getPackageInfo(Object value, CDOPackageRegistry packageRegistry)
{
if (value instanceof EPackage)
{
return packageRegistry.getPackageInfo((EPackage)value);
}
if (value instanceof CDOPackageInfo)
{
CDOPackageInfo packageInfo = (CDOPackageInfo)value;
if (packageInfo.getPackageUnit().getPackageRegistry() == packageRegistry)
{
return packageInfo;
}
}
return null;
}
/**
* Returns additional CDO infos for an {@link EClass}.
* <p>
* This operation is somewhat expensive because it synchronizes on the EClass and iterates over all adapters.
* Whenever possible use {@link CDORevision#getClassInfo()} or <code>InternalCDOObject.getClassInfo()</code>.
*
* @since 2.0
*/
public static CDOClassInfo getClassInfo(EClass eClass)
{
synchronized (eClass)
{
EList<Adapter> adapters = eClass.eAdapters();
CDOClassInfo classInfo = (CDOClassInfo)EcoreUtil.getAdapter(adapters, CDOClassInfo.class);
if (classInfo == null)
{
classInfo = new CDOClassInfoImpl();
adapters.add(0, classInfo);
}
return classInfo;
}
}
/**
* Returns all persistent {@link EStructuralFeature features} of an {@link EClass}.
* <p>
* This operation is somewhat expensive because it synchronizes on the EClass and iterates over all adapters.
*
* @since 2.0
* @deprecated As of 4.2 use <code>CDOModelUtil.getClassInfo(eClass).getAllPersistentFeatures()</code>.
* @see #getClassInfo(EClass)
*/
@Deprecated
public static EStructuralFeature[] getAllPersistentFeatures(EClass eClass)
{
CDOClassInfo classInfo = getClassInfo(eClass);
return classInfo.getAllPersistentFeatures();
}
/**
* @since 2.0
*/
public static CDOPackageUnit createPackageUnit()
{
return new CDOPackageUnitImpl();
}
/**
* @since 2.0
*/
public static CDOPackageInfo createPackageInfo()
{
return new CDOPackageInfoImpl();
}
/**
* @since 3.0
*/
public static EPackage readPackage(ExtendedDataInput in, ResourceSet resourceSet, boolean lookForResource) throws IOException
{
String uri = in.readString();
boolean zipped = in.readBoolean();
byte[] bytes = in.readByteArray();
return EMFUtil.createEPackage(uri, bytes, zipped, resourceSet, lookForResource);
}
/**
* @since 2.0
*/
public static void writePackage(ExtendedDataOutput out, EPackage ePackage, boolean zipped, EPackage.Registry packageRegistry) throws IOException
{
checkCrossResourceURIs(ePackage);
byte[] bytes = EMFUtil.getEPackageBytes(ePackage, zipped, packageRegistry);
out.writeString(ePackage.getNsURI());
out.writeBoolean(zipped);
out.writeByteArray(bytes);
}
/**
* @since 3.0
*/
public static void checkCrossResourceURIs(EPackage ePackage)
{
TreeIterator<EObject> it = ePackage.eAllContents();
while (it.hasNext())
{
EObject packageElement = it.next();
if (packageElement 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
{
for (EObject referencedElement : packageElement.eCrossReferences())
{
EObject referenceTarget = null;
if (referencedElement.eIsProxy())
{
referencedElement = EcoreUtil.resolve(referencedElement, (ResourceSet)null);
if (referencedElement.eIsProxy())
{
String msg = "Package '%s' contains unresolved proxy '%s'";
msg = String.format(msg, ePackage.getNsURI(), ((InternalEObject)referencedElement).eProxyURI());
throw new IllegalStateException(msg);
}
}
if (referencedElement.eResource() != null && referencedElement.eResource() != packageElement.eResource())
{
// It's a reference into another resource.
EPackage referencedPackage = null;
if (referencedElement instanceof EClassifier)
{
referenceTarget = referencedElement;
referencedPackage = ((EClassifier)referencedElement).getEPackage();
}
else if (referencedElement instanceof EStructuralFeature)
{
referenceTarget = referencedElement;
EStructuralFeature feature = (EStructuralFeature)referencedElement;
EClass ownerClass = (EClass)feature.eContainer();
referencedPackage = ownerClass.getEPackage();
}
else if (referencedElement instanceof EGenericType)
{
EGenericType genType = (EGenericType)referencedElement;
EClassifier c = genType.getEClassifier();
if (c != null)
{
referenceTarget = c;
referencedPackage = c.getEPackage();
}
}
if (referencedPackage == null)
{
continue;
}
while (referencedPackage.getESuperPackage() != null)
{
referencedPackage = referencedPackage.getESuperPackage();
}
String resourceURI = referenceTarget.eResource().getURI().toString();
if (!resourceURI.toString().equals(referencedPackage.getNsURI()))
{
String msg = "URI of the resource (%s) does not match the nsURI (%s) of the top-level package;\n"
+ "this can be fixed by calling Resource.setURI(URI) after loading the packages,\n"
+ "or by configuring a URI mapping from nsURI's to location URI's before loading the packages,\n"
+ "and then loading them with their nsURI's";
msg = String.format(msg, resourceURI, referencedPackage.getNsURI());
throw new IllegalStateException(msg);
}
}
}
}
}
}
/**
* @since 4.10
*/
public static void checkNoFeatureMaps(StringBuilder builder, EPackage ePackage, boolean checkFeatureMapEntries)
{
for (TreeIterator<EObject> it = ePackage.eAllContents(); it.hasNext();)
{
EObject packageElement = it.next();
if (checkFeatureMapEntries && packageElement instanceof EClassifier)
{
EClassifier classifier = (EClassifier)packageElement;
if (FeatureMapUtil.isFeatureMapEntry(classifier))
{
builder.append(String.format("Package '%s' contains feature map entry '%s'", ePackage.getNsURI(), classifier.getName()));
builder.append("\n");
}
}
else if (packageElement instanceof EStructuralFeature)
{
EStructuralFeature feature = (EStructuralFeature)packageElement;
if (FeatureMapUtil.isFeatureMap(feature))
{
EClass eClass = feature.getEContainingClass();
builder.append(String.format("Package '%s' contains feature map '%s.%s'", ePackage.getNsURI(), eClass.getName(), feature.getName()));
builder.append("\n");
}
}
}
}
/**
* @since 4.10
*/
public static void checkNoFeatureMaps(InternalCDOPackageUnit[] packageUnits, boolean checkFeatureMapEntries) throws CDOException
{
StringBuilder builder = new StringBuilder();
for (InternalCDOPackageUnit packageUnit : packageUnits)
{
EPackage ePackage = packageUnit.getTopLevelPackageInfo().getEPackage();
checkNoFeatureMaps(builder, ePackage, checkFeatureMapEntries);
}
if (builder.length() != 0)
{
throw new CDOException(builder.toString());
}
}
/**
* @since 4.0
*/
public static Map<EClass, List<EClass>> getSubTypes(EPackage.Registry packageRegistry)
{
Map<EClass, List<EClass>> result = new HashMap<>();
for (String nsURI : packageRegistry.keySet())
{
EPackage ePackage = packageRegistry.getEPackage(nsURI);
getSubTypes(ePackage, result);
}
return result;
}
private static void getSubTypes(EPackage ePackage, Map<EClass, List<EClass>> result)
{
for (EClassifier classifier : ePackage.getEClassifiers())
{
if (classifier instanceof EClass)
{
EClass eClass = (EClass)classifier;
getSubType(eClass, ROOT_TYPE, result);
for (EClass eSuperType : eClass.getEAllSuperTypes())
{
getSubType(eClass, eSuperType, result);
}
}
}
}
private static void getSubType(EClass eClass, EClass eSuperType, Map<EClass, List<EClass>> result)
{
if (eSuperType.eIsProxy())
{
OM.LOG.warn("getSubTypes encountered a proxy EClass which will be ignored: " + eSuperType);
return;
}
List<EClass> list = result.get(eSuperType);
if (list == null)
{
list = new ArrayList<>();
result.put(eSuperType, list);
}
list.add(eClass);
}
}