blob: 28b1e94be8c9ec298b3a1bf22c941e376e7464e8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012, 2018 IBM Corporation, Zeligsoft Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - Initial API and implementation
* Zeligsoft - Bug 252600
* Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Bug 297666
* Axel Uhl (SAP AG) - Bug 342644
*******************************************************************************/
package org.eclipse.ocl.ecore.internal;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.ecore.EcoreEnvironment;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.EcoreFactory;
import org.eclipse.ocl.ecore.EcorePackage;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.types.AnyType;
import org.eclipse.ocl.types.ElementType;
import org.eclipse.ocl.types.InvalidType;
import org.eclipse.ocl.types.MessageType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.PrimitiveType;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.utilities.PredefinedType;
/**
* Implementation of the {@link OCLStandardLibrary} for the Ecore environment.
*
* @author Christian W. Damus (cdamus)
*/
public final class OCLStandardLibraryImpl implements OCLStandardLibrary<EClassifier> {
private static final String NS_URI = EcoreEnvironment.OCL_STANDARD_LIBRARY_NS_URI;
private static EClassifier OCL_ANY;
private static EClassifier OCL_ELEMENT;
private static EClassifier OCL_BOOLEAN;
private static EClassifier OCL_INTEGER;
private static EClassifier OCL_UNLIMITED_NATURAL;
private static EClassifier OCL_REAL;
private static EClassifier OCL_STRING;
private static EClassifier OCL_VOID;
private static EClassifier OCL_MESSAGE;
private static EClassifier OCL_TYPE;
private static EClassifier OCL_INVALID;
private static EClassifier OCL_T;
private static EClassifier OCL_T2;
private static EClassifier OCL_SET;
private static EClassifier OCL_ORDERED_SET;
private static EClassifier OCL_BAG;
private static EClassifier OCL_SEQUENCE;
private static EClassifier OCL_COLLECTION;
private static EClassifier STATE;
private static EClassifier OCL_EXPRESSION;
// this must be initialized ahead of stdlibPackage, which depends on it
/** The shared instance of the OCL Standard Library for the UML environment. */
public static final OCLStandardLibraryImpl INSTANCE = new OCLStandardLibraryImpl();
/** The package containing the OCL Standard Library classifiers. */
public static EPackage stdlibPackage = init();
/** The singleton instance of the <tt>OclInvalid</tt> standard library type. */
// public static final EObject INVALID = stdlibPackage.getEFactoryInstance().create(
// (EClass) stdlibPackage.getEClassifier("OclInvalid_Class")); //$NON-NLS-1$
public static final EObject INVALID = new EObjectImpl()
{
public String toString() { return "invalid"; } //$NON-NLS-1$
};
// not instantiable by clients
private OCLStandardLibraryImpl() {
super();
}
public EClassifier getBoolean() {
return OCL_BOOLEAN;
}
public EClassifier getInteger() {
return OCL_INTEGER;
}
public EClassifier getUnlimitedNatural() {
return OCL_UNLIMITED_NATURAL;
}
public EClassifier getOclInvalid() {
return OCL_INVALID;
}
public EClassifier getReal() {
return OCL_REAL;
}
public EClassifier getString() {
return OCL_STRING;
}
public EClassifier getOclAny() {
return OCL_ANY;
}
public EClassifier getOclElement() {
return OCL_ELEMENT;
}
public Object getInvalid() {
return INVALID;
}
public EClassifier getState() {
return STATE;
}
public EClassifier getOclMessage() {
return OCL_MESSAGE;
}
public EClassifier getOclType() {
return OCL_TYPE;
}
public EClassifier getOclVoid() {
return OCL_VOID;
}
public EClassifier getT() {
return OCL_T;
}
public EClassifier getT2() {
return OCL_T2;
}
public EClassifier getSet() {
return OCL_SET;
}
public EClassifier getOrderedSet() {
return OCL_ORDERED_SET;
}
public EClassifier getBag() {
return OCL_BAG;
}
public EClassifier getSequence() {
return OCL_SEQUENCE;
}
public EClassifier getCollection() {
return OCL_COLLECTION;
}
public EClassifier getOclExpression() {
return OCL_EXPRESSION;
}
private static EPackage init() {
if (stdlibPackage != null) {
return stdlibPackage;
}
URI oclStandardLibraryNsURI = URI.createURI(NS_URI);
URI libraryURI = URIConverter.URI_MAP.get(oclStandardLibraryNsURI);
if (libraryURI == null) {
// standalone case: no library specified, we generate the built-in library.
return build();
}
URI oclPluginURI = URI.createPlatformPluginURI("org.eclipse.ocl.ecore/model/oclstdlib.ecore", true); //$NON-NLS-1$
if (oclPluginURI.equals(libraryURI)) {
// normal plugin case: we generate the built-in library.
return build();
}
// Unusual case, try to load an explicit library model
ResourceSet rset = new ResourceSetImpl();
// Ensure that an EcoreResource factory is registered for the ecore extension.
// Note that when running standalone, a registration in the global registry is not certain.
OCL.initialize(rset);
Resource res = null;
try {
Resource load = rset.getResource(oclStandardLibraryNsURI, true);
// transfer the loaded resource contents to a new resource that
// decodes URI fragments when resolving objects
res = OCLEcorePlugin.getEcoreResourceFactory().createResource(oclStandardLibraryNsURI);
res.getContents().addAll(load.getContents());
stdlibPackage = (EPackage) res.getContents().get(0);
OCL_ANY = stdlibPackage.getEClassifier(AnyType.SINGLETON_NAME);
OCL_ELEMENT = stdlibPackage.getEClassifier(ElementType.SINGLETON_NAME);
OCL_BOOLEAN = stdlibPackage.getEClassifier(PrimitiveType.BOOLEAN_NAME);
OCL_INTEGER = stdlibPackage.getEClassifier(PrimitiveType.INTEGER_NAME);
OCL_UNLIMITED_NATURAL = stdlibPackage.getEClassifier(PrimitiveType.UNLIMITED_NATURAL_NAME);
OCL_REAL = stdlibPackage.getEClassifier(PrimitiveType.REAL_NAME);
OCL_STRING = stdlibPackage.getEClassifier(PrimitiveType.STRING_NAME);
OCL_VOID = stdlibPackage.getEClassifier(VoidType.SINGLETON_NAME);
OCL_MESSAGE = stdlibPackage.getEClassifier(MessageType.SINGLETON_NAME);
OCL_INVALID = stdlibPackage.getEClassifier(InvalidType.SINGLETON_NAME);
OCL_T = stdlibPackage.getEClassifier("T"); //$NON-NLS-1$
OCL_T2 = stdlibPackage.getEClassifier("T2"); //$NON-NLS-1$
OCL_TYPE = (EClassifier) EcoreUtil.getObjectByType(
stdlibPackage.getEClassifiers(),
EcorePackage.Literals.TYPE_TYPE);
OCL_SET = (EClassifier) EcoreUtil.getObjectByType(
stdlibPackage.getEClassifiers(),
EcorePackage.Literals.SET_TYPE);
OCL_ORDERED_SET = (EClassifier) EcoreUtil.getObjectByType(
stdlibPackage.getEClassifiers(),
EcorePackage.Literals.ORDERED_SET_TYPE);
OCL_BAG = (EClassifier) EcoreUtil.getObjectByType(
stdlibPackage.getEClassifiers(),
EcorePackage.Literals.BAG_TYPE);
OCL_SEQUENCE = (EClassifier) EcoreUtil.getObjectByType(
stdlibPackage.getEClassifiers(),
EcorePackage.Literals.SEQUENCE_TYPE);
// don't use EcoreUtil because the other collection types would match
OCL_COLLECTION = stdlibPackage.getEClassifier("Collection(T)"); //$NON-NLS-1$
STATE = stdlibPackage.getEClassifier("State"); //$NON-NLS-1$
OCL_EXPRESSION = stdlibPackage.getEClassifier("OclExpression"); //$NON-NLS-1$
EPackage.Registry.INSTANCE.put(stdlibPackage.getNsURI(), stdlibPackage);
return stdlibPackage;
} catch (Exception e) {
// unusual case: the library file isn't there, fallback to
// generating it on the fly.
return build();
} finally {
if (res != null) {
// don't want this resource to be in a resource set
rset.getResources().remove(res);
}
}
}
// this method is used to build the standard library when not loading it
// from file
private static EPackage build() {
if (stdlibPackage != null) {
return stdlibPackage;
}
Environment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?> old = Environment.Registry.INSTANCE.getEnvironmentFor(
EcorePackage.Literals.ANY_TYPE);
stdlibPackage = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEPackage();
stdlibPackage.setName("oclstdlib"); //$NON-NLS-1$
stdlibPackage.setNsPrefix("oclstdlib"); //$NON-NLS-1$
stdlibPackage.setNsURI(NS_URI);
Resource.Factory factory = OCLEcorePlugin.getEcoreResourceFactory();
Resource res = factory.createResource(URI.createURI(stdlibPackage.getNsURI()));
res.getContents().add(stdlibPackage);
Environment.Registry.INSTANCE.deregisterEnvironment(old);
EcoreEnvironment env =
(EcoreEnvironment) EcoreEnvironmentFactory.INSTANCE.loadEnvironment(
res);
Environment.Registry.INSTANCE.registerEnvironment(env);
OCL_ANY = EcoreFactory.eINSTANCE.createAnyType();
OCL_ELEMENT = EcoreFactory.eINSTANCE.createElementType();
OCL_BOOLEAN = EcoreFactory.eINSTANCE.createPrimitiveType();
OCL_BOOLEAN.setName(PrimitiveType.BOOLEAN_NAME);
OCL_INTEGER = EcoreFactory.eINSTANCE.createPrimitiveType();
OCL_INTEGER.setName(PrimitiveType.INTEGER_NAME);
OCL_UNLIMITED_NATURAL = EcoreFactory.eINSTANCE.createPrimitiveType();
OCL_UNLIMITED_NATURAL.setName(PrimitiveType.UNLIMITED_NATURAL_NAME);
OCL_REAL = EcoreFactory.eINSTANCE.createPrimitiveType();
OCL_REAL.setName(PrimitiveType.REAL_NAME);
OCL_STRING = EcoreFactory.eINSTANCE.createPrimitiveType();
OCL_STRING.setName(PrimitiveType.STRING_NAME);
OCL_VOID = EcoreFactory.eINSTANCE.createVoidType();
OCL_MESSAGE = EcoreFactory.eINSTANCE.createMessageType();
OCL_INVALID = EcoreFactory.eINSTANCE.createInvalidType();
OCL_T = EcoreFactory.eINSTANCE.createAnyType();
OCL_T.setName("T"); //$NON-NLS-1$
OCL_T2 = EcoreFactory.eINSTANCE.createAnyType();
OCL_T2.setName("T2"); //$NON-NLS-1$
OCL_TYPE = (EClassifier) OCLFactoryImpl.INSTANCE.createTypeType(OCL_T);
OCL_SET = (EClassifier) OCLFactoryImpl.INSTANCE.createSetType(OCL_T);
OCL_ORDERED_SET = (EClassifier) OCLFactoryImpl.INSTANCE.createOrderedSetType(OCL_T);
OCL_BAG = (EClassifier) OCLFactoryImpl.INSTANCE.createBagType(OCL_T);
OCL_SEQUENCE = (EClassifier) OCLFactoryImpl.INSTANCE.createSequenceType(OCL_T);
OCL_COLLECTION = (EClassifier) OCLFactoryImpl.INSTANCE.createCollectionType(OCL_T);
STATE = EcoreFactory.eINSTANCE.createElementType();
STATE.setName("State"); //$NON-NLS-1$
OCL_EXPRESSION = EcoreFactory.eINSTANCE.createElementType();
OCL_EXPRESSION.setName("OclExpression"); //$NON-NLS-1$
OCL_BOOLEAN.setInstanceClass(Boolean.class);
OCL_STRING.setInstanceClass(String.class);
OCL_INTEGER.setInstanceClass(Integer.class);
OCL_UNLIMITED_NATURAL.setInstanceClass(Integer.class);
OCL_REAL.setInstanceClass(Double.class);
register(OCL_ANY).getEOperations().addAll(
OCLStandardLibraryUtil.createAnyTypeOperations(env));
register(OCL_VOID).getEOperations().addAll(
OCLStandardLibraryUtil.createAnyTypeOperations(env));
register(OCL_INVALID).getEOperations().addAll(
OCLStandardLibraryUtil.createAnyTypeOperations(env));
register(OCL_BOOLEAN).getEOperations().addAll(
OCLStandardLibraryUtil.createBooleanOperations(env));
register(OCL_INTEGER).getEOperations().addAll(
OCLStandardLibraryUtil.createIntegerOperations(env));
register(OCL_UNLIMITED_NATURAL).getEOperations().addAll(
OCLStandardLibraryUtil.createUnlimitedNaturalOperations(env));
register(OCL_REAL).getEOperations().addAll(
OCLStandardLibraryUtil.createRealOperations(env));
register(OCL_STRING).getEOperations().addAll(
OCLStandardLibraryUtil.createStringOperations(env));
register(OCL_TYPE).getEOperations().addAll(
OCLStandardLibraryUtil.createTypeTypeOperations(env));
register(OCL_MESSAGE).getEOperations().addAll(
OCLStandardLibraryUtil.createMessageTypeOperations(env));
register(OCL_ELEMENT);
register(STATE);
register(OCL_EXPRESSION);
List<EOperation> operations;
List<EOperation> iterators;
operations = register(OCL_COLLECTION).getEOperations();
operations.addAll(OCLStandardLibraryUtil.createCollectionOperations(env));
iterators = OCLStandardLibraryUtil.createCollectionIterators(env);
stereotypeAsIterator(iterators);
operations.addAll(iterators);
operations = register(OCL_SET).getEOperations();
operations.addAll(OCLStandardLibraryUtil.createSetOperations(env));
iterators = OCLStandardLibraryUtil.createSetIterators(env);
stereotypeAsIterator(iterators);
operations.addAll(iterators);
operations = register(OCL_ORDERED_SET).getEOperations();
operations.addAll(OCLStandardLibraryUtil.createOrderedSetOperations(env));
iterators = OCLStandardLibraryUtil.createOrderedSetIterators(env);
stereotypeAsIterator(iterators);
operations.addAll(iterators);
operations = register(OCL_BAG).getEOperations();
operations.addAll(OCLStandardLibraryUtil.createBagOperations(env));
iterators = OCLStandardLibraryUtil.createBagIterators(env);
stereotypeAsIterator(iterators);
operations.addAll(iterators);
operations = register(OCL_SEQUENCE).getEOperations();
operations.addAll(OCLStandardLibraryUtil.createSequenceOperations(env));
iterators = OCLStandardLibraryUtil.createSequenceIterators(env);
stereotypeAsIterator(iterators);
operations.addAll(iterators);
register(OCL_T); // operations already defined by OclAny
register(OCL_T2); // operations already defined by OclAny
EPackage.Registry.INSTANCE.put(stdlibPackage.getNsURI(), stdlibPackage);
Environment.Registry.INSTANCE.registerEnvironment(old);
return stdlibPackage;
}
private static EClass register(EClassifier stdType) {
EClass result = (stdType instanceof EClass)? (EClass) stdType : null;
// add the type to the standard library package
stdlibPackage.getEClassifiers().add(stdType);
if ((stdType instanceof PredefinedType<?>) && !(stdType instanceof EClass)) {
// an EClass would store its own operations; this cannot.
// Create a shadow class to store the operations
result = createShadowClass(stdType);
stdlibPackage.getEClassifiers().add(result);
}
return result;
}
public static EClassifier getOwner(EOperation operation) {
EClass ownerClass = operation.getEContainingClass();
EClassifier result = ownerClass;
if (ownerClass != null) {
EClassifier shadowed = getRealClassifier(ownerClass);
if (shadowed != null) {
result = shadowed;
}
}
return result;
}
public static EClassifier getOwner(EStructuralFeature property) {
EClass ownerClass = property.getEContainingClass();
EClassifier result = ownerClass;
if (ownerClass != null) {
EClassifier shadowed = getRealClassifier(ownerClass);
if (shadowed != null) {
result = shadowed;
}
}
return result;
}
/**
* Creates the shadow class to contain features that an Ecore classifier
* cannot contain for itself.
*
* @param classifier an Ecore classifier
*
* @return the class containing its features
*/
public static EClass createShadowClass(EClassifier classifier) {
// the features may have invalid characters in their names
EClass result = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEClass();
result.setName(classifier.getName() + "_Class"); //$NON-NLS-1$
EAnnotation ann = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEAnnotation();
ann.setSource(Environment.OCL_NAMESPACE_URI);
ann.getReferences().add(classifier);
result.getEAnnotations().add(ann);
return result;
}
/**
* Finds the shadow class to contain features defined for the
* specified OCL <code>type</code>, if it already exists.
*
* @param classifier an Ecore classifier
* @param pkg the package in which to look for the shadow class
*
* @return the class containing its features, or <code>null</code> if not
* found
*/
public static EClass findShadowClass(EClassifier classifier, EPackage pkg) {
for (EClassifier next : pkg.getEClassifiers()) {
if (next instanceof EClass) {
EClass eclass = (EClass) next;
EAnnotation ann = eclass.getEAnnotation(
Environment.OCL_NAMESPACE_URI);
if ((ann != null) && ann.getReferences().contains(classifier)) {
return eclass;
}
}
}
return null;
}
public static EClassifier getRealClassifier(EClass shadowClass) {
EClassifier result = null;
EAnnotation ann = shadowClass.getEAnnotation(
Environment.OCL_NAMESPACE_URI);
if ((ann != null) && !ann.getReferences().isEmpty()) {
result = (EClassifier) ann.getReferences().get(0);
}
return result;
}
/**
* Obtains the existing operations of the specified type, stored in it
* or in a shadow class. <b>Note</b> that this method returns
* <code>null</code>, not an empty list, if none are found.
*
* @param type an OCL pre-defined type
* @return its existing operations, or <code>null</code> if none are found
*/
public static EList<EOperation> getExistingOperations(EClassifier type) {
EList<EOperation> result = null;
if (type instanceof EClass) {
result = ((EClass) type).getEOperations();
} else {
EPackage pkg = type.getEPackage();
if (pkg != null) {
EClass shadow = findShadowClass(type, pkg);
if (shadow != null) {
result = shadow.getEOperations();
}
}
}
return (result == null)? ECollections.<EOperation>emptyEList() : result;
}
/**
* Marks the specified operations as being collection iterators (as distinct
* from ordinary operations).
*
* @param operations operations to designate as iterators
*/
public static void stereotypeAsIterator(Collection<EOperation> operations) {
for (EOperation oper : operations) {
EAnnotation ann = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEAnnotation();
ann.setSource(Environment.OCL_NAMESPACE_URI);
ann.getDetails().put("keywords", "iterator"); //$NON-NLS-1$ //$NON-NLS-2$
oper.getEAnnotations().add(ann);
}
}
/**
* Selects from the specified operations those that are collection iterators.
*
* @param operations operations
* @return the subset that are stereotyped as iterators
*/
public static EList<EOperation> selectIterators(Collection<EOperation> operations) {
EList<EOperation> result = new BasicEList.FastCompare<EOperation>();
for (EOperation oper : operations) {
EAnnotation ann = oper.getEAnnotation(Environment.OCL_NAMESPACE_URI);
if ((ann != null) && "iterator".equals(ann.getDetails().get( //$NON-NLS-1$
"keywords"))) { //$NON-NLS-1$
result.add(oper);
}
}
return result;
}
}