| /******************************************************************************* |
| * Copyright (c) 2005, 2018 IBM Corporation 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 |
| * Adolfo Sanchez-Barbudo Herrera - 222581 generic collection type resolution |
| *******************************************************************************/ |
| package org.eclipse.ocl; |
| |
| 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.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.impl.ResourceImpl; |
| import org.eclipse.ocl.expressions.CollectionKind; |
| import org.eclipse.ocl.expressions.Variable; |
| import org.eclipse.ocl.types.BagType; |
| import org.eclipse.ocl.types.CollectionType; |
| import org.eclipse.ocl.types.MessageType; |
| import org.eclipse.ocl.types.OrderedSetType; |
| import org.eclipse.ocl.types.SequenceType; |
| import org.eclipse.ocl.types.SetType; |
| import org.eclipse.ocl.types.TupleType; |
| import org.eclipse.ocl.types.TypeType; |
| import org.eclipse.ocl.types.util.TypesSwitch; |
| import org.eclipse.ocl.util.ObjectUtil; |
| import org.eclipse.ocl.util.TypeUtil; |
| import org.eclipse.ocl.utilities.OCLFactory; |
| import org.eclipse.ocl.utilities.TypedElement; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| |
| |
| /** |
| * A partial implementation of the {@link TypeResolver} interface, useful for |
| * providers of environment implementations to build their type resolvers. |
| * <p> |
| * It is recommended that clients extend this class to customize resolution of |
| * types based on their models, rather than implementing the interface. Simply |
| * override a few protected methods for creating metamodel-specific constructs. |
| * </p> |
| * <p> |
| * See the {@link Environment} class for a description of the |
| * generic type parameters of this class. |
| * </p> |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| public abstract class AbstractTypeResolver<PK, C, O, P, PM> |
| implements TypeResolver<C, O, P> { |
| |
| protected static final String COLLECTIONS_PACKAGE = "collections"; //$NON-NLS-1$ |
| protected static final String MESSAGES_PACKAGE = "messages"; //$NON-NLS-1$ |
| protected static final String TUPLES_PACKAGE = "tuples"; //$NON-NLS-1$ |
| protected static final String TYPES_PACKAGE = "types"; //$NON-NLS-1$ |
| protected static final String ADDITIONS_PACKAGE = "additions"; //$NON-NLS-1$ |
| |
| private final TypesSwitch<C> resolveSwitch = new ResolveSwitch(); |
| |
| private final Environment<PK, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> env; |
| private final OCLFactory oclFactory; |
| private final UMLReflection<PK, C, O, P, ?, PM, ?, ?, ?, ?> uml; |
| |
| private Resource resource; |
| |
| // whether I should dispose my resource because I created it |
| private boolean shouldDisposeResource; |
| |
| private PK collectionPackage; |
| private PK tuplePackage; |
| private PK typePackage; |
| private PK messagePackage; |
| private PK additionalFeaturesPackage; |
| |
| private CollectionType<C, O> collection; |
| private SetType<C, O> set; |
| private OrderedSetType<C, O> orderedSet; |
| private BagType<C, O> bag; |
| private SequenceType<C, O> sequence; |
| private TypeType<C, O> oclType; |
| private MessageType<C, O, P> oclMessage; |
| |
| /** |
| * Initializes me with an environment. I create my own resource for |
| * persistence of model-based types. |
| * |
| * @param env the environment that I persist |
| */ |
| public AbstractTypeResolver( |
| Environment<PK, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> env) { |
| this(env, null); |
| } |
| |
| /** |
| * Initializes me with a resource in which I will persist the model-based |
| * types that I generate in my associated {@link Environment}. |
| * |
| * @param env my environment |
| * @param resource my resource |
| */ |
| public AbstractTypeResolver( |
| Environment<PK, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> env, |
| Resource resource) { |
| this.env = env; |
| this.resource = resource; |
| |
| oclFactory = env.getOCLFactory(); |
| uml = env.getUMLReflection(); |
| } |
| |
| // Documentation copied from the inherited specification |
| public C resolve(C type) { |
| C result = (type == null)? type : resolveSwitch.doSwitch((EObject) type); |
| |
| if ((result != null) && (result != type)) { |
| // dispose the old type; it won't be used |
| ObjectUtil.dispose(type); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Obtains the environment that I persist. |
| * |
| * @return my environment |
| */ |
| protected final Environment<PK, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> getEnvironment() { |
| return env; |
| } |
| |
| // Documentation copied from the inherited specification |
| public Resource getResource() { |
| if (resource == null) { |
| // because we are creating the resource, we should dispose it |
| shouldDisposeResource = true; |
| resource = createResource(); |
| } |
| |
| return resource; |
| } |
| |
| /** |
| * Creates the resource that persists my generated types. Subclasses requiring |
| * persistence must override this default implementation, as it creates a |
| * resource that does not support persistence and does not have a useful URI. |
| * A subclass could even find some other resource, such as the model on |
| * which constraints are being parsed, if desired. |
| * |
| * @return the new resource |
| */ |
| protected Resource createResource() { |
| return new ResourceImpl(URI.createURI("ocl:///oclenv")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Obtains the package containing the collection types that I generate. |
| * |
| * @return my collection type package |
| */ |
| public PK getCollectionPackage() { |
| if (collectionPackage == null) { |
| collectionPackage = findCollectionPackage(); |
| if (collectionPackage == null) { |
| collectionPackage = createCollectionPackage(); |
| } |
| } |
| |
| return collectionPackage; |
| } |
| |
| /** |
| * Creates the package containing the collection types that I generate. |
| * |
| * @return the new collection type package |
| */ |
| protected PK createCollectionPackage() { |
| return createPackage(COLLECTIONS_PACKAGE); |
| } |
| |
| /** |
| * Finds the package storing collection types, if our resource already |
| * contains it. |
| * |
| * @return the existing collection types package, or <code>null</code> |
| * if it does not yet exist |
| */ |
| protected PK findCollectionPackage() { |
| return findPackage(COLLECTIONS_PACKAGE); |
| } |
| |
| // Documentation copied from the inherited specification |
| public CollectionType<C, O> resolveCollectionType( |
| CollectionKind kind, |
| C elementType) { |
| |
| CollectionType<C, O> result; |
| |
| if (elementType == getCollection().getElementType()) { |
| switch (kind) { |
| case SET_LITERAL : |
| result = getSet(); |
| break; |
| case ORDERED_SET_LITERAL: |
| result = getOrderedSet(); |
| break; |
| case SEQUENCE_LITERAL: |
| result = getSequence(); |
| break; |
| case BAG_LITERAL: |
| result = getBag(); |
| break; |
| default: |
| result = getCollection(); |
| break; |
| } |
| } else { |
| result = findCollectionType(kind, elementType); |
| } |
| |
| if (result == null) { |
| result = createCollectionType(kind, elementType); |
| } |
| |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private CollectionType<C, O> getCollection() { |
| if (collection == null) { |
| collection = (CollectionType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getCollection(); |
| } |
| |
| return collection; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private SetType<C, O> getSet() { |
| if (set == null) { |
| set = (SetType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getSet(); |
| } |
| |
| return set; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private OrderedSetType<C, O> getOrderedSet() { |
| if (orderedSet == null) { |
| orderedSet = (OrderedSetType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getOrderedSet(); |
| } |
| |
| return orderedSet; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private SequenceType<C, O> getSequence() { |
| if (sequence == null) { |
| sequence = (SequenceType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getSequence(); |
| } |
| |
| return sequence; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private BagType<C, O> getBag() { |
| if (bag == null) { |
| bag = (BagType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getBag(); |
| } |
| |
| return bag; |
| } |
| |
| /** |
| * Creates a new collection type of the specified <code>kind</code> and element |
| * type, assuming that it does not already exist. |
| * |
| * @param kind the kind of collection to create |
| * @param elementType the collection's element type |
| * |
| * @return the new collection type |
| */ |
| @SuppressWarnings("unchecked") |
| protected CollectionType<C, O> createCollectionType( |
| CollectionKind kind, C elementType) { |
| |
| CollectionType<C, O> result = |
| oclFactory.createCollectionType(kind, elementType); |
| addClassifier(getCollectionPackage(), (C) result); |
| |
| return result; |
| } |
| |
| /** |
| * Finds an existing collection type matching the specified <code>kind</code> and |
| * element type, if any has already been created. |
| * |
| * @param kind the element kind to search for |
| * @param elementType the element type to search for |
| * |
| * @return the existing collection type, or <code>null</code> if none found |
| */ |
| protected CollectionType<C, O> findCollectionType( |
| CollectionKind kind, C elementType) { |
| |
| for (C next : uml.getClassifiers(getCollectionPackage())) { |
| if (next instanceof CollectionType<?, ?>) { |
| @SuppressWarnings("unchecked") |
| CollectionType<C, O> type = |
| (CollectionType<C, O>) next; |
| |
| if ((type.getKind() == kind) && |
| (TypeUtil.getRelationship( |
| env, |
| type.getElementType(), |
| elementType) == UMLReflection.SAME_TYPE)) { |
| return type; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Obtains the package containing the tuple types that I generate. |
| * |
| * @return my tuple type package |
| */ |
| public PK getTuplePackage() { |
| if (tuplePackage == null) { |
| tuplePackage = findTuplePackage(); |
| if (tuplePackage == null) { |
| tuplePackage = createTuplePackage(); |
| } |
| } |
| |
| return tuplePackage; |
| } |
| |
| /** |
| * Creates the package containing the tuple types that I generate. |
| * |
| * @return the new tuple type package |
| */ |
| protected PK createTuplePackage() { |
| return createPackage(TUPLES_PACKAGE); |
| } |
| |
| /** |
| * Finds the package storing tuple types, if our resource already |
| * contains it. |
| * |
| * @return the existing tuple types package, or <code>null</code> |
| * if it does not yet exist |
| */ |
| protected PK findTuplePackage() { |
| return findPackage(TUPLES_PACKAGE); |
| } |
| |
| // Documentation copied from the inherited specification |
| public TupleType<O, P> resolveTupleType( |
| EList<? extends TypedElement<C>> parts) { |
| TupleType<O, P> result = findTupleType(parts); |
| |
| if (result == null) { |
| result = createTupleType(parts); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Creates a new tuple type from the specified <code>parts</code>, assuming that |
| * it does not already exist. |
| * |
| * @param parts the {@link TypedElement}s describing the tuple parts |
| * |
| * @return the new tuple type |
| */ |
| @SuppressWarnings("unchecked") |
| protected TupleType<O, P> createTupleType( |
| EList<? extends TypedElement<C>> parts) { |
| TupleType<O, P> result = |
| oclFactory.createTupleType(parts); |
| |
| addClassifier(getTuplePackage(), (C) result); |
| return result; |
| } |
| |
| /** |
| * Finds an existing tuple type matching the specified <code>parts</code>, if any |
| * has already been created. |
| * |
| * @param parts the {@link TypedElement}s describing the tuple parts |
| * |
| * @return the existing tuple type, or <code>null</code> if none found |
| */ |
| protected TupleType<O, P> findTupleType( |
| EList<? extends TypedElement<C>> parts) { |
| for (C next : uml.getClassifiers(getTuplePackage())) { |
| if (next instanceof TupleType<?, ?>) { |
| @SuppressWarnings("unchecked") |
| TupleType<O, P> type = |
| (TupleType<O, P>) next; |
| |
| if (type.oclProperties().size() == parts.size()) { |
| boolean match = true; |
| |
| for (TypedElement<C> part : parts) { |
| @SuppressWarnings("unchecked") |
| P property = env.lookupProperty((C) type, part.getName()); |
| |
| if ((property == null) || |
| (TypeUtil.getRelationship( |
| env, |
| resolve(uml.getOCLType(property)), |
| part.getType()) != UMLReflection.SAME_TYPE)) { |
| // this isn't the tuple type we're looking for |
| match = false; |
| break; |
| } |
| } |
| |
| if (match) { |
| // this must be the tuple type we're looking for |
| return type; |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Obtains the package containing the type types that I generate. |
| * |
| * @return my type type package |
| */ |
| public PK getTypePackage() { |
| if (typePackage == null) { |
| typePackage = findTypePackage(); |
| if (typePackage == null) { |
| typePackage = createTypePackage(); |
| } |
| } |
| |
| return typePackage; |
| } |
| |
| /** |
| * Creates the package containing the type types that I generate. |
| * |
| * @return the new type type package |
| */ |
| protected PK createTypePackage() { |
| return createPackage(TYPES_PACKAGE); |
| } |
| |
| /** |
| * Finds the package storing type types, if our resource already |
| * contains it. |
| * |
| * @return the existing type types package, or <code>null</code> |
| * if it does not yet exist |
| */ |
| protected PK findTypePackage() { |
| return findPackage(TYPES_PACKAGE); |
| } |
| |
| // Documentation copied from the inherited specification |
| public TypeType<C, O> resolveTypeType(C type) { |
| if (type == getOclType().getReferredType()) { |
| // this is the canonical OclType instance |
| return getOclType(); |
| } |
| |
| TypeType<C, O> result = findTypeType(type); |
| |
| if (result == null) { |
| result = createTypeType(type); |
| } |
| |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private TypeType<C, O> getOclType() { |
| if (oclType == null) { |
| oclType = (TypeType<C, O>) getEnvironment().getOCLStandardLibrary() |
| .getOclType(); |
| } |
| |
| return oclType; |
| } |
| |
| /** |
| * Creates a new type type for the specified <code>type</code>, |
| * assuming that it does not already exist. |
| * |
| * @param type the referenced model type |
| * |
| * @return the new type type |
| */ |
| @SuppressWarnings("unchecked") |
| protected TypeType<C, O> createTypeType(C type) { |
| TypeType<C, O> result = oclFactory.createTypeType(type); |
| |
| addClassifier(getTypePackage(), (C) result); |
| |
| return result; |
| } |
| |
| /** |
| * Finds an existing type type matching the specified <code>type</code>, |
| * if any has already been created. |
| * |
| * @param type the referenced model type |
| * |
| * @return the existing type type, or <code>null</code> if none found |
| */ |
| protected TypeType<C, O> findTypeType(C type) { |
| for (C next : uml.getClassifiers(getTypePackage())) { |
| if (next instanceof TypeType<?, ?>) { |
| @SuppressWarnings("unchecked") |
| TypeType<C, O> typeType = |
| (TypeType<C, O>) next; |
| |
| if (TypeUtil.getRelationship( |
| env, |
| typeType.getReferredType(), |
| type) == UMLReflection.SAME_TYPE) { |
| |
| return typeType; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Obtains the package containing the message types that I generate. |
| * |
| * @return my message type package |
| */ |
| public PK getMessagePackage() { |
| if (messagePackage == null) { |
| messagePackage = findMessagePackage(); |
| if (messagePackage == null) { |
| messagePackage = createMessagePackage(); |
| } |
| } |
| |
| return messagePackage; |
| } |
| |
| /** |
| * Creates the package containing the message types that I generate. |
| * |
| * @return the new message type package |
| */ |
| protected PK createMessagePackage() { |
| return createPackage(MESSAGES_PACKAGE); |
| } |
| |
| /** |
| * Finds the package storing message types, if our resource already |
| * contains it. |
| * |
| * @return the existing message types package, or <code>null</code> |
| * if it does not yet exist |
| */ |
| protected PK findMessagePackage() { |
| return findPackage(MESSAGES_PACKAGE); |
| } |
| |
| // Documentation copied from the inherited specification |
| public MessageType<C, O, P> resolveOperationMessageType(O operation) { |
| if (operation == getOclMessage().getReferredOperation()) { |
| // this is the canonical OclMessage type |
| return getOclMessage(); |
| } |
| |
| MessageType<C, O, P> result = findMessageType(operation); |
| |
| if (result == null) { |
| result = createOperationMessageType(operation); |
| } |
| |
| return result; |
| } |
| |
| // Documentation copied from the inherited specification |
| public MessageType<C, O, P> resolveSignalMessageType(C signal) { |
| if (signal == getOclMessage().getReferredSignal()) { |
| // this is the canonical OclMessage type |
| return getOclMessage(); |
| } |
| |
| MessageType<C, O, P> result = findMessageType(signal); |
| |
| if (result == null) { |
| result = createSignalMessageType(signal); |
| } |
| |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private MessageType<C, O, P> getOclMessage() { |
| if (oclMessage == null) { |
| oclMessage = (MessageType<C, O, P>) getEnvironment().getOCLStandardLibrary() |
| .getOclMessage(); |
| } |
| |
| return oclMessage; |
| } |
| |
| /** |
| * Creates a new message type for the specified <code>element</code>, |
| * assuming that it does not already exist. |
| * |
| * @param operation the operation referenced by the message type |
| * |
| * @return the new message type |
| */ |
| @SuppressWarnings("unchecked") |
| protected MessageType<C, O, P> createOperationMessageType(O operation) { |
| MessageType<C, O, P> result = |
| oclFactory.createOperationMessageType(operation); |
| addClassifier(getMessagePackage(), (C) result); |
| return result; |
| } |
| |
| /** |
| * Creates a new message type for the specified <code>element</code>, |
| * assuming that it does not already exist. |
| * |
| * @param signal the signal referenced by the message type |
| * |
| * @return the new message type |
| */ |
| @SuppressWarnings("unchecked") |
| protected MessageType<C, O, P> createSignalMessageType(C signal) { |
| MessageType<C, O, P> result = |
| oclFactory.createSignalMessageType(signal); |
| addClassifier(getMessagePackage(), (C) result); |
| return result; |
| } |
| |
| /** |
| * Finds an existing message type matching the specified <code>element</code>, |
| * if any has already been created. |
| * |
| * @param element the referenced model element |
| * |
| * @return the existing message type, or <code>null</code> if none found |
| */ |
| protected MessageType<C, O, P> findMessageType(Object element) { |
| for (C next : uml.getClassifiers(getMessagePackage())) { |
| if (next instanceof MessageType<?, ?, ?>) { |
| @SuppressWarnings("unchecked") |
| MessageType<C, O, P> type = |
| (MessageType<C, O, P>) next; |
| |
| if ((type.getReferredOperation() == element) |
| || (type.getReferredSignal() == element)) { |
| |
| return type; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Obtains the package containing the additional operations and properties |
| * parsed in my environment. |
| * |
| * @return my additional features package |
| */ |
| public PK getAdditionalFeaturesPackage() { |
| if (additionalFeaturesPackage == null) { |
| additionalFeaturesPackage = findAdditionalFeaturesPackage(); |
| if (additionalFeaturesPackage == null) { |
| additionalFeaturesPackage = createAdditionalFeaturesPackage(); |
| } |
| } |
| |
| return additionalFeaturesPackage; |
| } |
| |
| /** |
| * Creates the package containing the additional operations and properties |
| * parsed in my environment. |
| * |
| * @return the new additional features package |
| */ |
| protected PK createAdditionalFeaturesPackage() { |
| return createPackage(ADDITIONS_PACKAGE); |
| } |
| |
| /** |
| * Finds the package storing additional features, if our resource already |
| * contains it. |
| * |
| * @return the existing additional features package, or <code>null</code> |
| * if it does not yet exist |
| */ |
| protected PK findAdditionalFeaturesPackage() { |
| return findPackage(ADDITIONS_PACKAGE); |
| } |
| |
| // Documentation copied from the inherited specification |
| public O resolveAdditionalOperation(C owner, O operation) { |
| C shadow = getShadowClass(owner); |
| |
| O result = findMatchingOperation(shadow, operation); |
| if (result == null) { |
| result = operation; |
| addOperation(shadow, result); |
| } |
| |
| return result; |
| } |
| |
| public List<O> getAdditionalOperations(C owner) { |
| if (hasAdditionalFeatures()) { |
| C shadow = findShadowClass(owner); |
| |
| if (shadow != null) { |
| return uml.getOperations(shadow); |
| } |
| } |
| |
| return ECollections.emptyEList(); |
| } |
| |
| /** |
| * Finds an operation already existing in the specified <code>shadow</code> |
| * class that matches the specified <code>operation</code> signature. |
| * |
| * @param shadow the shadow class to search |
| * @param operation the operation to match |
| * |
| * @return the matching operation, or <code>null</code> if not found |
| */ |
| protected O findMatchingOperation(C shadow, O operation) { |
| String operationName = uml.getName(operation); |
| |
| for (O next : uml.getOperations(shadow)) { |
| if ((next == operation) |
| || (uml.getName(next).equals(operationName) |
| && matchParameters(next, operation))) { |
| return next; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Determines whether two operations have the same parameter signature. |
| * |
| * @param a an operation |
| * @param b another operation |
| * |
| * @return <code>true</code> if they have the same formal parameters in the |
| * same order; <code>false</code>, otherwise |
| */ |
| private boolean matchParameters(O a, O b) { |
| List<PM> aparms = uml.getParameters(a); |
| List<PM> bparms = uml.getParameters(b); |
| |
| if (aparms.size() == bparms.size()) { |
| int count = aparms.size(); |
| |
| for (int i = 0; i < count; i++) { |
| PM aparm = aparms.get(i); |
| PM bparm = bparms.get(i); |
| |
| if (!uml.getName(aparm).equals(uml.getName(bparm)) |
| || TypeUtil.getRelationship( |
| env, |
| resolve(uml.getOCLType(aparm)), |
| resolve(uml.getOCLType(bparm))) |
| != UMLReflection.SAME_TYPE) { |
| |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Finds a property already existing in the specified <code>shadow</code> |
| * class that matches the specified <code>property</code> signature. |
| * |
| * @param shadow the shadow class to search |
| * @param property the property to match |
| * |
| * @return the matching operation, or <code>null</code> if not found |
| */ |
| protected P findMatchingProperty(C shadow, P property) { |
| String propertyName = uml.getName(property); |
| |
| for (P next : uml.getAttributes(shadow)) { |
| if ((next == property) || uml.getName(next).equals(propertyName)) { |
| return next; |
| } |
| } |
| |
| return null; |
| } |
| |
| // Documentation copied from the inherited specification |
| public P resolveAdditionalAttribute(C owner, P property) { |
| C shadow = getShadowClass(owner); |
| |
| P result = findMatchingProperty(shadow, property); |
| if (result == null) { |
| result = property; |
| addProperty(shadow, result); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Queries whether the current environment has any additional features |
| * defined at all. |
| * |
| * @return whether I have any additional features |
| */ |
| protected boolean hasAdditionalFeatures() { |
| // if I was loaded from an existing resource, I may not yet have looked |
| // for my additional-features package |
| return (additionalFeaturesPackage != null) |
| || (findAdditionalFeaturesPackage() != null); |
| } |
| |
| public List<P> getAdditionalAttributes(C owner) { |
| if (hasAdditionalFeatures()) { |
| C shadow = findShadowClass(owner); |
| |
| if (shadow != null) { |
| return uml.getAttributes(shadow); |
| } |
| } |
| |
| return ECollections.emptyEList(); |
| } |
| |
| /** |
| * Creates the shadow class to contain additional features defined for the |
| * specified OCL <code>type</code>. |
| * |
| * @param type an OCL type |
| * |
| * @return the class containing its additional features |
| */ |
| protected abstract C createShadowClass(C type); |
| |
| /** |
| * Adds the specified property to a classifier. |
| * |
| * @param owner the classifier to own the property |
| * @param property the property to add to the classifier |
| */ |
| protected abstract void addProperty(C owner, P property); |
| |
| /** |
| * Adds the specified operation to a classifier. |
| * |
| * @param owner the classifier to own the operation |
| * @param operation the operation to add to the classifier |
| */ |
| protected abstract void addOperation(C owner, O operation); |
| |
| /** |
| * Finds the shadow class to contain additional features defined for the |
| * specified OCL <code>type</code>, if it already exists. |
| * |
| * @param type an OCL type |
| * |
| * @return the class containing its additional features, or <code>null</code> |
| * if not found |
| */ |
| protected C findShadowClass(C type) { |
| PK pkg = hasAdditionalFeatures()? getAdditionalFeaturesPackage() : null; |
| |
| if (pkg != null) { |
| for (C next : uml.getClassifiers(pkg)) { |
| if (getShadowedClassifier(next) == type) { |
| return next; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Obtains the classifier for which the specified shadow stores additional |
| * features. |
| * |
| * @param shadow a class originally created to shadow a model classifier |
| * |
| * @return the model classifier that it supports |
| * |
| * @see #createShadowClass(Object) |
| * @see #getShadowClass(Object) |
| */ |
| protected abstract C getShadowedClassifier(C shadow); |
| |
| /** |
| * Obtains the shadow class for the specified model classifier, creating |
| * it if necessary. The shadow class will store attributes and operations |
| * on behalf of this model classifier. |
| * |
| * @param type a classifier in the model |
| * @return its shadow |
| */ |
| protected C getShadowClass(C type) { |
| C result = findShadowClass(type); |
| |
| if (result == null) { |
| result = createShadowClass(type); |
| |
| PK pkg = getAdditionalFeaturesPackage(); |
| |
| if (pkg != null) { |
| addClassifier(pkg, result); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Creates a package with the specified name. This puts the new package |
| * in an appropriate place in my {@linkplain #getResource() resource}. |
| * |
| * @param name the package name |
| * @return the new package, persisted in my resource |
| */ |
| protected abstract PK createPackage(String name); |
| |
| /** |
| * Finds the existing package with the specified name in my resource. |
| * This will be one of the packages that I would create for storing |
| * OCL-generated types. |
| * |
| * @param name the package to seek |
| * @return the package, or <code>null</code> if none |
| */ |
| protected abstract PK findPackage(String name); |
| |
| /** |
| * Adds a classifier to the specified package, which is one that I use to |
| * store OCL-generated types. |
| * |
| * @param pkg one of my packages |
| * @param classifier a classifier to add to it |
| */ |
| protected abstract void addClassifier(PK pkg, C classifier); |
| |
| /** |
| * A type switch that resolves types against my resolver's environment. |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| @SuppressWarnings("unchecked") |
| private class ResolveSwitch extends TypesSwitch<C> { |
| @Override |
| public <C1, O1> C caseCollectionType(CollectionType<C1, O1> object) { |
| return (C) resolveCollectionType(object.getKind(), |
| resolve((C) object.getElementType())); |
| } |
| |
| @Override |
| public <O1, P1> C caseTupleType(TupleType<O1, P1> object) { |
| return (C) resolveTupleType(createTupleParts( |
| (EList<P>) object.oclProperties())); |
| } |
| |
| @Override |
| public <C1, O1> C caseTypeType(TypeType<C1, O1> object) { |
| return (C) resolveTypeType(resolve((C) object.getReferredType())); |
| } |
| |
| @Override |
| public <C1, O1, P1> C caseMessageType(MessageType<C1, O1, P1> object) { |
| if (object.getReferredOperation() != null) { |
| return (C) resolveOperationMessageType((O) object.getReferredOperation()); |
| } else if (object.getReferredSignal() != null) { |
| return (C) resolveSignalMessageType((C) object.getReferredSignal()); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * In the default case, the classifier is defined by the user model or |
| * by the OCL Standard Library, so it isn't persisted in the environment. |
| * |
| * @param object a classifier |
| * @return the same classifier |
| */ |
| @Override |
| public C defaultCase(EObject object) { |
| return (C) object; |
| } |
| |
| private EList<Variable<C, PM>> createTupleParts( |
| Collection<P> properties) { |
| EList<Variable<C, PM>> result = |
| new BasicEList.FastCompare<Variable<C, PM>>(); |
| |
| OCLFactory oclFactory = env.getOCLFactory(); |
| |
| for (P next : properties) { |
| Variable<C, PM> v = oclFactory.createVariable(); |
| uml.setName(v, uml.getName(next)); |
| uml.setType(v, resolve(uml.getOCLType(next))); |
| result.add(v); |
| } |
| |
| return result; |
| } |
| } |
| |
| /** |
| * Disposes me by unloading my resource, if and only if I created it in |
| * the first place. If I was loaded from an existing resource, then it is |
| * the client's responsibility to manage it. |
| * |
| * @since 1.2 |
| */ |
| public void dispose() { |
| if (shouldDisposeResource && (resource != null)) { |
| if (resource.isLoaded()) { |
| resource.unload(); |
| } |
| |
| if (resource.getResourceSet() != null) { |
| resource.getResourceSet().getResources().remove(resource); |
| } |
| |
| resource = null; |
| } |
| } |
| } |