| /** |
| * Copyright (c) 2008, 2014, 2011 IBM Corporation, Zeligsoft Inc., Open Canarias S.L., 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: |
| * IBM - Original org.eclipse.ocl.util.TypeUtil API and implementation |
| * Open Canarias - Bug 233673 Refactoring to support type checking extensibility |
| * Zeligsoft - Bugs 244886, 245619, 233673, 179990 |
| * Stefan Schulze - Bug 245619 |
| * Adolfo Sanchez-Barbudo Herrera - Bug 260403. |
| * Axel Uhl (SAP AG) - Bug 342644 |
| * |
| */ |
| |
| package org.eclipse.ocl; |
| |
| import static org.eclipse.ocl.utilities.UMLReflection.SAME_TYPE; |
| import static org.eclipse.ocl.utilities.UMLReflection.STRICT_SUBTYPE; |
| import static org.eclipse.ocl.utilities.UMLReflection.STRICT_SUPERTYPE; |
| import static org.eclipse.ocl.utilities.UMLReflection.SUBTYPE; |
| import static org.eclipse.ocl.utilities.UMLReflection.UNRELATED_TYPE; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.ocl.expressions.CollectionKind; |
| import org.eclipse.ocl.expressions.TypeExp; |
| import org.eclipse.ocl.expressions.Variable; |
| import org.eclipse.ocl.internal.l10n.OCLMessages; |
| import org.eclipse.ocl.lpg.BasicEnvironment; |
| import org.eclipse.ocl.options.ParsingOptions; |
| import org.eclipse.ocl.types.AnyType; |
| import org.eclipse.ocl.types.BagType; |
| import org.eclipse.ocl.types.CollectionType; |
| import org.eclipse.ocl.types.MessageType; |
| import org.eclipse.ocl.types.OCLStandardLibrary; |
| import org.eclipse.ocl.types.OrderedSetType; |
| import org.eclipse.ocl.types.PrimitiveType; |
| 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.util.OCLStandardLibraryUtil; |
| import org.eclipse.ocl.util.OCLUtil; |
| import org.eclipse.ocl.util.ObjectUtil; |
| import org.eclipse.ocl.util.TypeUtil; |
| import org.eclipse.ocl.utilities.OCLFactory; |
| import org.eclipse.ocl.utilities.PredefinedType; |
| import org.eclipse.ocl.utilities.TypedElement; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| |
| /** |
| * <p> |
| * Implementation of the {@link TypeChecker} interface which offers the default |
| * implementation for the type checking system. This class encapsulates the |
| * behaviour of the 1.2 release's {@link TypeUtil} class, in an extensible unit. |
| * </p> |
| * <p> |
| * This class is not intended to be extended by clients. They should extend |
| * {@link AbstractTypeResolver} instead, so that {@link TypeChecker} can be |
| * successfully adapted by {@link AbstractEnvironment}. However, if an already |
| * implemented {@link TypeResolver} wants to exploit the type checking system |
| * extensibility, it may extend this class. |
| * </p> |
| * <p> |
| * See {@link Environment} for generic parameters description |
| * </p> |
| * |
| * @author Adolfo Sanchez-Barbudo Herrera (adolfosbh) |
| * |
| * @since 1.3 |
| * |
| * @see AbstractTypeResolver |
| * @see TypeUtil |
| */ |
| public abstract class AbstractTypeChecker<C, O, P, PM> |
| implements TypeChecker<C, O, P> { |
| |
| private static final Set<String> RELATIONAL_OPERATORS; |
| |
| static { |
| RELATIONAL_OPERATORS = new java.util.HashSet<String>(); |
| RELATIONAL_OPERATORS.add(PredefinedType.LESS_THAN_NAME); |
| RELATIONAL_OPERATORS.add(PredefinedType.LESS_THAN_EQUAL_NAME); |
| RELATIONAL_OPERATORS.add(PredefinedType.GREATER_THAN_NAME); |
| RELATIONAL_OPERATORS.add(PredefinedType.GREATER_THAN_EQUAL_NAME); |
| } |
| |
| private final Environment<?, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> env; |
| |
| private final UMLReflection<?, C, O, P, ?, PM, ?, ?, ?, ?> uml; |
| |
| private final OCLFactory oclFactory; |
| |
| private final OCLStandardLibrary<C> stdlib; |
| |
| private final C implicitRootClass; |
| |
| /** |
| * Initializes me with my environment. |
| * |
| * @param env |
| * my environment |
| */ |
| public AbstractTypeChecker( |
| Environment<?, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> env) { |
| super(); |
| this.env = env; |
| uml = env.getUMLReflection(); |
| oclFactory = env.getOCLFactory(); |
| stdlib = env.getOCLStandardLibrary(); |
| C optionValue = ParsingOptions.getValue(env, ParsingOptions.implicitRootClass(env)); |
| // check that, if there is a value for this option, it is a class |
| if ((optionValue != null) && !uml.isClass(optionValue)) { |
| implicitRootClass = null; |
| } |
| else { |
| implicitRootClass = optionValue; |
| } |
| } |
| |
| /** |
| * Obtains my OCL parsing environment. |
| * |
| * @return my environment |
| */ |
| protected Environment<?, C, O, P, ?, PM, ?, ?, ?, ?, ?, ?> getEnvironment() { |
| return env; |
| } |
| |
| /** |
| * Obtains the UML introspector that I use to access the subject model. |
| * |
| * @return my UML introspector |
| */ |
| protected UMLReflection<?, C, O, P, ?, PM, ?, ?, ?, ?> getUMLReflection() { |
| return uml; |
| } |
| |
| /** |
| * Obtains the OCL factory that I use to instantiate the OCL Abstract |
| * Syntax. |
| * |
| * @return my OCL factory |
| */ |
| protected OCLFactory getOCLFactory() { |
| return oclFactory; |
| } |
| |
| public int getRelationship(C type1, C type2) { |
| |
| if (type1 != null) { |
| type1 = uml.getOCLType(type1); |
| } |
| if (type2 != null) { |
| type2 = uml.getOCLType(type2); |
| } |
| |
| // simplest case is when the types actually are the same |
| if (type1 == type2) { |
| return SAME_TYPE; |
| } |
| |
| // OclVoid makes for easy cases |
| if ((type1 == stdlib.getOclVoid()) && (type2 != stdlib.getOclInvalid())) { |
| return STRICT_SUBTYPE; |
| } else if ((type2 == stdlib.getOclVoid()) |
| && (type1 != stdlib.getOclInvalid())) { |
| return STRICT_SUPERTYPE; |
| } |
| |
| // so does OclInvalid |
| if (type1 == stdlib.getOclInvalid()) { |
| return STRICT_SUBTYPE; |
| } else if (type2 == stdlib.getOclInvalid()) { |
| return STRICT_SUPERTYPE; |
| } |
| |
| // and so does OclAny, also |
| if (type1 instanceof AnyType) { |
| return type2 instanceof AnyType |
| ? SAME_TYPE : |
| (type2 instanceof CollectionType<?, ?>) |
| ? UNRELATED_TYPE |
| : STRICT_SUPERTYPE; |
| } else if (type2 instanceof AnyType) { |
| return (type1 instanceof CollectionType<?, ?>) |
| ? UNRELATED_TYPE |
| : STRICT_SUBTYPE; |
| } |
| |
| // handle primitive types |
| // From OCL 2.3 (11.5.1): "Note that UnlimitedNatural is a subclass |
| // of Integer and that Integer is a subclass of Real" |
| if (type1 instanceof PrimitiveType<?>) { |
| int type1Order = (type1==stdlib.getUnlimitedNatural()?0:type1==stdlib.getInteger()?1:type1==stdlib.getReal()?2:-1); |
| int type2Order = (type2==stdlib.getUnlimitedNatural()?0:type2==stdlib.getInteger()?1:type2==stdlib.getReal()?2:-1); |
| if (type1Order >= 0 && type2Order >= 0) { |
| if (type1Order < type2Order) { |
| return STRICT_SUBTYPE; |
| } else if (type1Order > type2Order) { |
| return STRICT_SUPERTYPE; |
| } |
| } |
| return UNRELATED_TYPE; |
| } else if (type2 instanceof PrimitiveType<?>) { |
| // tested all possible primitive type conformances in the other case |
| return UNRELATED_TYPE; |
| } |
| |
| // handle collection types |
| if (type1 instanceof CollectionType<?, ?>) { |
| if (!(type2 instanceof CollectionType<?, ?>)) { |
| return UNRELATED_TYPE; |
| } |
| |
| @SuppressWarnings("unchecked") |
| CollectionType<C, O> first = (CollectionType<C, O>) type1; |
| @SuppressWarnings("unchecked") |
| CollectionType<C, O> other = (CollectionType<C, O>) type2; |
| |
| int kindRelationship = getRelationship(first.getKind(), other |
| .getKind()); |
| |
| if (kindRelationship == UNRELATED_TYPE) { |
| return kindRelationship; |
| } |
| |
| int elementRelationship = getRelationship(first.getElementType(), |
| other.getElementType()); |
| |
| switch (kindRelationship) { |
| case SAME_TYPE : |
| return elementRelationship; |
| case STRICT_SUBTYPE : |
| switch (elementRelationship) { |
| case SAME_TYPE : |
| case STRICT_SUBTYPE : |
| return STRICT_SUBTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| case STRICT_SUPERTYPE : |
| switch (elementRelationship) { |
| case SAME_TYPE : |
| case STRICT_SUPERTYPE : |
| return STRICT_SUPERTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| default : |
| return UNRELATED_TYPE; |
| } |
| } |
| |
| if (type1 instanceof MessageType<?, ?, ?> && type2 == stdlib.getOclMessage()) { |
| return STRICT_SUBTYPE; |
| } else if (type2 instanceof MessageType<?, ?, ?> |
| && type1 == stdlib.getOclMessage()) { |
| return STRICT_SUPERTYPE; |
| } |
| |
| if (type1 instanceof TypeType<?, ?> && type2 == stdlib.getOclType()) { |
| return STRICT_SUBTYPE; |
| } else if (type2 instanceof TypeType<?, ?> && type1 == stdlib.getOclType()) { |
| return STRICT_SUPERTYPE; |
| } |
| |
| if (type1 instanceof TupleType<?, ?> || type2 instanceof TupleType<?, ?>) { |
| if (!((type1 instanceof TupleType<?, ?>) && (type2 instanceof TupleType<?, ?>))) { |
| return UNRELATED_TYPE; |
| } |
| |
| List<P> props1 = uml.getAttributes(type1); |
| List<P> props2 = uml.getAttributes(type2); |
| |
| if (props1.size() != props2.size()) { |
| return UNRELATED_TYPE; |
| } |
| |
| int result = SAME_TYPE; // assume properties are the same. |
| |
| Iterator<P> iter1 = props1.iterator(); |
| while (iter1.hasNext()) { |
| P prop1 = iter1.next(); |
| boolean found = false; |
| |
| Iterator<P> iter2 = props2.iterator(); |
| while (iter2.hasNext()) { |
| P prop2 = iter2.next(); |
| |
| if (uml.getName(prop1).equals(uml.getName(prop2))) { |
| /* |
| * The feature types must conform in the same direction. |
| */ |
| int propResult = getRelationship(resolve(uml |
| .getOCLType(prop1)), resolve(uml.getOCLType(prop2))); |
| |
| if (result == SAME_TYPE) { |
| result = propResult; |
| } else if (result != propResult) { |
| return UNRELATED_TYPE; |
| } |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return UNRELATED_TYPE; |
| } |
| } |
| |
| return result; |
| } |
| |
| // exhausted the possibilities for pre-defined types |
| if (type1 instanceof PredefinedType<?> || type2 instanceof PredefinedType<?>) { |
| return UNRELATED_TYPE; |
| } |
| |
| // remaining case is pure model element types. The environment must |
| // handle this |
| |
| int result = uml.getRelationship(type1, type2); |
| |
| if (result == UNRELATED_TYPE) { |
| // try the implicit root class |
| if ((implicitRootClass != null) && uml.isClass(type1) |
| && uml.isClass(type2)) { |
| if (type1 == implicitRootClass) { |
| result = STRICT_SUPERTYPE; |
| } else if (type2 == implicitRootClass) { |
| result = STRICT_SUBTYPE; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Get-relationship helper comparing collection kinds. |
| */ |
| protected int getRelationship(CollectionKind kind1, CollectionKind kind2) { |
| switch (kind1) { |
| case BAG_LITERAL : |
| switch (kind2) { |
| case BAG_LITERAL : |
| return SAME_TYPE; |
| case COLLECTION_LITERAL : |
| return STRICT_SUBTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| case SET_LITERAL : |
| switch (kind2) { |
| case SET_LITERAL : |
| return SAME_TYPE; |
| case ORDERED_SET_LITERAL : |
| return STRICT_SUPERTYPE; |
| case COLLECTION_LITERAL : |
| return STRICT_SUBTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| case ORDERED_SET_LITERAL : |
| switch (kind2) { |
| case ORDERED_SET_LITERAL : |
| return SAME_TYPE; |
| case SET_LITERAL : |
| case COLLECTION_LITERAL : |
| return STRICT_SUBTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| case SEQUENCE_LITERAL : |
| switch (kind2) { |
| case SEQUENCE_LITERAL : |
| return SAME_TYPE; |
| case COLLECTION_LITERAL : |
| return STRICT_SUBTYPE; |
| default : |
| return UNRELATED_TYPE; |
| } |
| default : |
| switch (kind2) { |
| case COLLECTION_LITERAL : |
| return SAME_TYPE; |
| default : |
| return STRICT_SUPERTYPE; |
| } |
| } |
| } |
| |
| public C getResultType(Object problemObject, C owner, O operation, |
| List<? extends TypedElement<C>> args) { |
| |
| C actualOwner = uml.getOwningClassifier(operation); |
| if (isStandardLibraryFeature(actualOwner, operation)) { |
| int opcode = OCLStandardLibraryUtil.getOperationCode(uml |
| .getName(operation)); |
| |
| if (opcode > 0) { |
| // OCL Standard Library operation. Not customizable |
| C result = OCLStandardLibraryUtil.getResultTypeOf( |
| problemObject, env, owner, opcode, args); |
| |
| if ((result == null) && (owner != actualOwner)) { |
| // the actual owner s different from the declared owner. |
| // This happens when we re-interpret user-defined types as |
| // corresponding OCL-defined types |
| result = OCLStandardLibraryUtil.getResultTypeOf( |
| problemObject, env, actualOwner, opcode, args); |
| } |
| |
| return result; |
| } |
| } |
| |
| return resolve(uml.getOCLType(operation)); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public C getPropertyType(C owner, P property) { |
| |
| C result = resolve(uml.getOCLType(property)); |
| |
| if (uml.isAssociationClass(owner) |
| && uml.getMemberEnds(owner).contains(property)) { |
| |
| // from the perspective of the association class, its ends have |
| // multiplicity 1 regardless of their definition (which is from |
| // the perspective of the classifiers at the ends) |
| if (result instanceof CollectionType<?, ?>) { |
| result = ((CollectionType<C, O>) result).getElementType(); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Get the common supertype of two types. This operation accounts for the |
| * OCL Standard Library types, which the otherwise similar |
| * {@link UMLReflection#getCommonSuperType(Object, Object)} method does not. |
| * |
| * @param type1 |
| * a type |
| * @param type2 |
| * another type |
| * @return their common supertype, if any, null if the two types have no |
| * common supertype |
| * |
| * @see UMLReflection#getCommonSuperType(Object, Object) |
| */ |
| @SuppressWarnings("unchecked") |
| public C commonSuperType(Object problemObject, C type1, C type2) { |
| |
| if (type1 != null) { |
| type1 = uml.asOCLType(type1); |
| } |
| if (type2 != null) { |
| type2 = uml.asOCLType(type2); |
| } |
| |
| if (ObjectUtil.equal(type1, type2)) { |
| return type2; |
| } |
| |
| // the generic type T represents the dynamic type against which we |
| // are comparing |
| if (type1 == stdlib.getT()) { |
| return type2; |
| } else if (type2 == stdlib.getT()) { |
| return type1; |
| } |
| |
| if ((type1 == stdlib.getOclVoid()) || (type1 == stdlib.getOclInvalid())) { |
| return type2; |
| } |
| if ((type2 == stdlib.getOclVoid()) || (type2 == stdlib.getOclInvalid())) { |
| return type1; |
| } |
| |
| if (type1 == stdlib.getOclAny() && !(type2 instanceof CollectionType<?, ?>)) { |
| return type1; |
| } |
| if (type2 == stdlib.getOclAny() && !(type1 instanceof CollectionType<?, ?>)) { |
| return type2; |
| } |
| |
| if ((type1 == stdlib.getInteger() || type1 == stdlib |
| .getUnlimitedNatural()) |
| && type2 == stdlib.getReal()) { |
| return type2; |
| } |
| if ((type2 == stdlib.getInteger() || type2 == stdlib |
| .getUnlimitedNatural()) |
| && type1 == stdlib.getReal()) { |
| return type1; |
| } |
| |
| if (type1 instanceof CollectionType<?, ?> && type2 instanceof CollectionType<?, ?>) { |
| |
| CollectionType<C, O> ct1 = (CollectionType<C, O>) type1; |
| |
| CollectionType<C, O> ct2 = (CollectionType<C, O>) type2; |
| |
| CollectionKind commonKind = commonSuperType(ct1.getKind(), ct2 |
| .getKind()); |
| |
| C resultElementType = commonSuperType(problemObject, ct1 |
| .getElementType(), ct2.getElementType()); |
| |
| return (C) resolveCollectionType(commonKind, resultElementType); |
| } |
| |
| if (type1 instanceof MessageType<?, ?, ?> && type2 instanceof MessageType<?, ?, ?>) { |
| return stdlib.getOclMessage(); |
| } |
| |
| if (type1 instanceof TypeType<?, ?> && type2 instanceof TypeType<?, ?>) { |
| return stdlib.getOclType(); |
| } |
| |
| if (type1 instanceof TupleType<?, ?> || type2 instanceof TupleType<?, ?>) { |
| if (!((type1 instanceof TupleType<?, ?>) && (type2 instanceof TupleType<?, ?>))) { |
| String message = OCLMessages.bind( |
| OCLMessages.TupleTypeMismatch_ERROR_, getName(type1), |
| getName(type2)); |
| error(message, "commonSuperType", problemObject); //$NON-NLS-1$ |
| return null; |
| } |
| |
| List<P> props1 = uml.getAttributes(type1); |
| List<P> props2 = uml.getAttributes(type2); |
| |
| if (props1.size() != props2.size()) { |
| String message = OCLMessages.bind( |
| OCLMessages.TupleFieldNumMismatch_ERROR_, getName(type1), |
| getName(type2)); |
| error(message, "commonSuperType", problemObject); //$NON-NLS-1$ |
| return null; |
| } |
| |
| EList<Variable<C, PM>> tupleParts = new BasicEList<Variable<C, PM>>(); |
| |
| for (P prop1 : props1) { |
| boolean found = false; |
| |
| for (P prop2 : props2) { |
| if (uml.getName(prop1).equals(uml.getName(prop2))) { |
| C resultElementType = commonSuperType(problemObject, |
| resolve(uml.getOCLType(prop1)), resolve(uml |
| .getOCLType(prop2))); |
| |
| found = true; |
| |
| Variable<C, PM> var = oclFactory.createVariable(); |
| uml.setName(var, getName(prop1)); |
| uml.setType(var, resultElementType); |
| |
| tupleParts.add(var); |
| break; |
| } |
| } |
| if (!found) { |
| String message = OCLMessages.bind( |
| OCLMessages.TupleFieldNotFound_ERROR_, new Object[]{ |
| getName(type1), getName(prop1), getName(type2)}); |
| error(message, "commonSuperType", problemObject); //$NON-NLS-1$ |
| return null; |
| } |
| } |
| |
| return (C) resolveTupleType(tupleParts); |
| } |
| |
| // once exhausted the possibilities for pre-defined types, |
| // if one of them is a CollectionType they don't have common super type |
| if (type1 instanceof CollectionType<?, ?> || type2 instanceof CollectionType<?, ?>) { |
| String message = OCLMessages.bind(OCLMessages.TypeMismatch_ERROR_, |
| getName(type1), getName(type2)); |
| error(message, "commonSuperType", problemObject); //$NON-NLS-1$ |
| return null; |
| } |
| |
| // remaining case is pure model element types. The environment must |
| // handle this |
| |
| C result = uml.getCommonSuperType(type1, type2); |
| |
| if (result == null) { |
| if ((implicitRootClass != null) && uml.isClass(type1) |
| && uml.isClass(type2)) { |
| result = implicitRootClass; |
| } |
| } |
| |
| // if common super type hasn't been found, OclAny is the common |
| // supertype |
| return (result == null) |
| ? stdlib.getOclAny() |
| : result; |
| } |
| |
| /** |
| * Common-supertype helper method for collection kinds |
| */ |
| protected CollectionKind commonSuperType(CollectionKind kind1, |
| CollectionKind kind2) { |
| |
| CollectionKind genericCollectionKind = CollectionKind.COLLECTION_LITERAL; |
| |
| switch (kind1) { |
| case BAG_LITERAL : |
| case SEQUENCE_LITERAL : |
| if (kind2 == kind1) { |
| return kind1; |
| } |
| |
| return genericCollectionKind; |
| case SET_LITERAL : |
| switch (kind2) { |
| case ORDERED_SET_LITERAL : |
| case SET_LITERAL : |
| return kind1; |
| default : |
| return genericCollectionKind; |
| } |
| case ORDERED_SET_LITERAL : |
| switch (kind2) { |
| case ORDERED_SET_LITERAL : |
| case SET_LITERAL : |
| return kind2; |
| default : |
| return genericCollectionKind; |
| } |
| default : |
| return genericCollectionKind; |
| } |
| } |
| |
| public boolean checkMutuallyComparable(Object problemObject, C type1, |
| C type2, int opcode) { |
| |
| // all of the primitive types are considered as mutually comparable |
| if (!(type1 instanceof PrimitiveType<?> && type2 instanceof PrimitiveType<?>)) { |
| if (commonSuperType(problemObject, type1, type2) == null) { |
| String message = OCLMessages.bind( |
| OCLMessages.Noncomforming_ERROR_, getName(type1), |
| OCLStandardLibraryUtil.getOperationName(opcode)); |
| error(message, "checkMutuallyComparable", problemObject); //$NON-NLS-1$ |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public boolean exactTypeMatch(C type1, C type2) { |
| |
| switch (getRelationship(type1, type2)) { |
| case SAME_TYPE : |
| return true; |
| default : |
| return false; |
| } |
| } |
| |
| public boolean compatibleTypeMatch(C type1, C type2) { |
| |
| switch (getRelationship(type1, type2)) { |
| case SAME_TYPE : |
| case STRICT_SUBTYPE : |
| return true; |
| default : |
| return false; |
| } |
| } |
| |
| public List<O> getOperations(C owner) { |
| List<O> result; |
| |
| if (owner instanceof TypeType<?, ?>) { |
| @SuppressWarnings("unchecked") |
| TypeType<C, O> source = (TypeType<C, O>) owner; |
| result = new java.util.ArrayList<O>(source.oclOperations()); |
| |
| // also include the static operations of the referred type |
| for (O o : getOperations(source.getReferredType())) { |
| if (uml.isStatic(o)) { |
| result.add(o); |
| } |
| } |
| |
| result = Collections.unmodifiableList(result); |
| } else { |
| if (owner instanceof PredefinedType<?>) { |
| @SuppressWarnings("unchecked") |
| PredefinedType<O> source = (PredefinedType<O>) owner; |
| result = new ArrayList<O>(source.oclOperations()); |
| |
| if ((source instanceof AnyType<?>) |
| && !ParsingOptions.getValue(env, |
| ParsingOptions.USE_COMPARE_TO_OPERATION)) { |
| // exclude the OclAny operations for <, <=, >, >= which |
| // should |
| // not be defined for OclAny |
| for (Iterator<O> iter = result.iterator(); iter.hasNext();) { |
| if (RELATIONAL_OPERATORS.contains(uml.getName(iter |
| .next()))) { |
| iter.remove(); |
| } |
| } |
| } |
| } else { |
| // it's a user type. Try to convert it to an OCL standard type |
| owner = uml.asOCLType(owner); |
| |
| if (owner instanceof PredefinedType<?>) { |
| @SuppressWarnings("unchecked") |
| PredefinedType<O> pt = (PredefinedType<O>) owner; |
| result = new ArrayList<O>(pt.oclOperations()); |
| } else { |
| result = new ArrayList<O>(); |
| |
| // Include both the AnyType operations (oclIsKindOf, etc) |
| // and the operations of the class itself. |
| |
| C oclAny = stdlib.getOclAny(); |
| |
| result.addAll(uml.getOperations(owner)); |
| |
| if ((implicitRootClass != null) |
| && (implicitRootClass != owner)) { |
| result.addAll(uml.getOperations(implicitRootClass)); |
| } |
| |
| result.addAll(getOperations(oclAny)); |
| } |
| } |
| |
| List<O> additionalOperations = env.getAdditionalOperations(owner); |
| if (additionalOperations != null && !additionalOperations.isEmpty()) { |
| result.addAll(additionalOperations); |
| } |
| |
| result = Collections.unmodifiableList(result); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Finds the most specific (re)definition of an attribute in the specified |
| * classifier. |
| * |
| * @param owner |
| * the classifier to search |
| * @param name |
| * the name of the operation |
| * |
| * @return the matching operation, or <code>null</code> if not found |
| */ |
| public P findAttribute(C owner, String name) { |
| List<P> attributes = getAttributes(owner); |
| List<P> matches = null; |
| |
| for (P attr : attributes) { |
| if (name.equals(uml.getName(attr))) { |
| if (uml.getOwningClassifier(attr) == owner) { |
| return attr; // obviously the most specific definition |
| } |
| |
| if (matches == null) { |
| // assume a small number of redefinitions |
| matches = new java.util.ArrayList<P>(3); |
| } |
| |
| matches.add(attr); |
| } |
| } |
| |
| if (matches != null) { |
| if (matches.size() == 1) { |
| return matches.get(0); |
| } else if (!matches.isEmpty()) { |
| return mostSpecificRedefinition(matches); |
| } |
| } |
| |
| return null; |
| } |
| |
| public List<P> getAttributes(C owner) { |
| List<P> result; |
| |
| if (owner instanceof TypeType<?, ?>) { |
| @SuppressWarnings("unchecked") |
| TypeType<C, O> source = (TypeType<C, O>) owner; |
| result = new java.util.ArrayList<P>(); |
| |
| // include the static properties of the referred type |
| for (P p : getAttributes(source.getReferredType())) { |
| if (uml.isStatic(p)) { |
| result.add(p); |
| } |
| } |
| |
| result = Collections.unmodifiableList(result); |
| } else { |
| if (owner instanceof PredefinedType<?>) { |
| result = new java.util.ArrayList<P>(uml.getAttributes(owner)); |
| } else { |
| // it's a user type. Try to convert it to an OCL standard type |
| owner = uml.asOCLType(owner); |
| |
| if (owner instanceof PredefinedType<?>) { |
| result = new java.util.ArrayList<P>(uml |
| .getAttributes(owner)); |
| } else { |
| result = new ArrayList<P>(); |
| |
| // Include both the AnyType properties defined by users as |
| // additional attributes and the properties of the class |
| // itself. |
| |
| C oclAny = stdlib.getOclAny(); |
| |
| result.addAll(uml.getAttributes(owner)); |
| |
| if ((implicitRootClass != null) |
| && (implicitRootClass != owner)) { |
| result.addAll(uml.getAttributes(implicitRootClass)); |
| } |
| |
| result.addAll(getAttributes(oclAny)); |
| } |
| } |
| |
| List<P> additionalProperties = env.getAdditionalAttributes(owner); |
| if (additionalProperties != null && !additionalProperties.isEmpty()) { |
| result.addAll(additionalProperties); |
| } |
| |
| result = Collections.unmodifiableList(result); |
| } |
| |
| return result; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public O resolveGenericSignature(C owner, O oper) { |
| |
| String name = uml.getName(oper); |
| List<PM> parameters = uml.getParameters(oper); |
| List<String> paramNames = new java.util.ArrayList<String>(parameters |
| .size()); |
| List<C> paramTypes = new java.util.ArrayList<C>(parameters.size()); |
| List<Variable<C, PM>> args = new ArrayList<Variable<C, PM>>(parameters |
| .size()); |
| |
| for (PM param : parameters) { |
| paramNames.add(uml.getName(param)); |
| paramTypes.add(resolveGenericType(owner, resolve(uml |
| .getOCLType(param)), stdlib.getT())); |
| |
| C paramType = resolve(uml.getOCLType(param)); |
| |
| Variable<C, PM> var = oclFactory.createVariable(); |
| if (paramType instanceof TypeType<?, ?>) { |
| // need the referred type |
| TypeType<C, O> typeType = (TypeType<C, O>) paramType; |
| if (typeType.getReferredType() == null) { |
| var.setType(stdlib.getT()); // case of oclAsType() |
| } else { |
| var.setType(typeType.getReferredType()); |
| } |
| } else { |
| if (paramType instanceof CollectionType<?, ?>) { |
| CollectionType<C, O> ct = (CollectionType<C, O>) paramType; |
| |
| if (ct.getElementType() == stdlib.getT2()) { |
| // special handling for the Collection(T2) parameter |
| // of the product collection operation |
| paramType = (C) resolveCollectionType(ct.getKind(), |
| stdlib.getT()); |
| } |
| } |
| |
| var.setType(paramType); |
| } |
| args.add(var); |
| } |
| |
| C resultType = getResultType(oper, owner, oper, args); |
| |
| return uml.createOperation(name, resultType, paramNames, paramTypes); |
| } |
| |
| /** |
| * Resolves the generic type of an operation parameter (where it is |
| * <tt>T</tt> or <tt>T2</tt>) against the source type of the operation or |
| * the type of the actual argument, as appropriate. |
| * |
| * @param owner |
| * the operation source type |
| * @param paramType |
| * the operation parameter type |
| * @param argType |
| * the actual operation argument type |
| * |
| * @return the resolved parameter type |
| */ |
| @SuppressWarnings("unchecked") |
| protected C resolveGenericType(C owner, C paramType, C argType) { |
| |
| C result = paramType; |
| |
| if (result == stdlib.getT()) { |
| // substitute the owner type (or owner's element type in |
| // the case of a collection) |
| result = owner; |
| if (result instanceof CollectionType<?, ?>) { |
| result = ((CollectionType<C, O>) result).getElementType(); |
| |
| if (result == stdlib.getOclVoid()) { |
| // special case for empty collections, whose element type is |
| // OclVoid. |
| // We want any argument to be accepted, and to solely |
| // determine |
| // the element type of the resulting collection |
| result = stdlib.getT(); |
| } |
| } |
| } else if (result instanceof CollectionType<?, ?>) { |
| // handle generic collection operation with parameter of |
| // type <collectionKind>(T) |
| CollectionType<C, O> collType = (CollectionType<C, O>) result; |
| C elemType = collType.getElementType(); |
| |
| if (elemType == stdlib.getT()) { |
| C ownerMatch = owner; |
| if (ownerMatch instanceof CollectionType<?, ?>) { |
| ownerMatch = ((CollectionType<C, O>) ownerMatch) |
| .getElementType(); |
| |
| if (ownerMatch == stdlib.getOclVoid()) { |
| // special case for empty collections, whose element |
| // type is OclVoid. |
| // We want any argument to be accepted, and to solely |
| // determine |
| // the element type of the resulting collection |
| ownerMatch = stdlib.getT(); |
| } |
| } |
| |
| result = (C) resolveCollectionType(collType.getKind(), |
| ownerMatch); |
| } else if (elemType == stdlib.getT2()) { |
| // wildard to match any other collection type |
| C argMatch = argType; |
| if (argMatch instanceof CollectionType<?, ?>) { |
| argMatch = ((CollectionType<C, O>) argMatch) |
| .getElementType(); |
| |
| if (argMatch == stdlib.getOclVoid()) { |
| // special case for empty collections, whose element |
| // type is OclVoid. |
| // We want any argument to be accepted, and to solely |
| // determine |
| // the element type of the resulting collection. Note |
| // that we |
| // want T, not T2, because it is our wildcard |
| argMatch = stdlib.getT(); |
| } |
| } |
| |
| result = (C) resolveCollectionType(collType.getKind(), argMatch); |
| } |
| } |
| |
| return result; |
| } |
| |
| public boolean matchArgs(C owner, List<?> paramsOrProperties, |
| List<? extends TypedElement<C>> args) { |
| int exactitude = matchArgsWithExactitude(owner, paramsOrProperties, args); |
| return exactitude >= 0; |
| } |
| |
| /** |
| * Return the number of arguments with exact type matches, or -ve if there is no match. |
| * @since 3.2 |
| */ |
| protected int matchArgsWithExactitude(C owner, List<?> paramsOrProperties, |
| List<? extends TypedElement<C>> args) { |
| int argsize; |
| |
| if (args == null) { |
| argsize = 0; |
| } else { |
| argsize = args.size(); |
| } |
| |
| if (paramsOrProperties.size() != argsize) { |
| return -1; |
| } |
| int exactitude = 0; |
| int i = 0; |
| for (Object paramOrProperty : paramsOrProperties) { |
| @SuppressWarnings("null") |
| TypedElement<C> arg = args.get(i++); |
| C argType = arg.getType(); |
| C popType = resolve(uml.getOCLType(paramOrProperty)); |
| |
| // handle parameters of type OclType |
| if (popType instanceof TypeType<?, ?>) { |
| if (arg instanceof TypeExp<?>) { |
| continue; |
| } |
| return -1; |
| } |
| |
| popType = resolveGenericType(owner, popType, argType); |
| |
| if (popType == stdlib.getT()) { |
| // this is a collection operation, and the collection is empty |
| // (element type is OclVoid). Any argument matches in this |
| // case, because any kind of element can be considered to not |
| // be in an empty collection |
| continue; |
| } |
| |
| int relationship = getRelationship(argType, popType); |
| if ((relationship & SUBTYPE) == 0) { |
| return -1; |
| } |
| if (relationship == SAME_TYPE) { |
| exactitude++; |
| } |
| } |
| return exactitude; |
| } |
| |
| public C findSignalMatching(C receiver, List<C> signals, String name, |
| List<? extends TypedElement<C>> args) { |
| |
| if (args == null) { |
| args = Collections.emptyList(); |
| } |
| |
| for (C signal : signals) { |
| if (uml.getName(signal).equals(name) |
| && matchArgs(receiver, uml.getAttributes(signal), args)) { |
| return signal; |
| } |
| } |
| |
| return null; |
| } |
| |
| public O findOperationMatching(C owner, String name, |
| List<? extends TypedElement<C>> args) { |
| |
| if (args == null) { |
| args = Collections.emptyList(); |
| } |
| |
| List<O> matches = getBestMatchingOperations(owner, name, args); |
| |
| if (matches != null) { |
| if (matches.size() == 1) { |
| return matches.get(0); |
| } else if (!matches.isEmpty()) { |
| return mostSpecificRedefinition(matches); |
| } |
| } |
| |
| // special handling for null and invalid values, whose types conform |
| // to all others |
| OCLStandardLibrary<C> lib = env.getOCLStandardLibrary(); |
| if ((owner == lib.getOclVoid()) || (owner == lib.getOclInvalid())) { |
| return findOperationForVoidOrInvalid(owner, name, args); |
| } |
| // special case for UnlimitedNatural < Integer because otherwise *.div(1) |
| // and similar can't be resolved |
| if (owner == lib.getUnlimitedNatural()) { |
| return findOperationMatching(lib.getInteger(), name, args); |
| } else if (owner instanceof SequenceType<?, ?> || |
| owner instanceof SetType<?, ?> || |
| owner instanceof BagType<?, ?> || |
| owner instanceof OrderedSetType<?, ?>) { |
| // nothing found in the specific collection class; perform |
| // lookup in Collection(T) |
| return findOperationMatching(lib.getCollection(), name, args); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @since 3.2 |
| */ |
| protected List<O> getBestMatchingOperations(C owner, String name, |
| List<? extends TypedElement<C>> args) { |
| List<O> operations = getOperations(owner); |
| List<O> matches = null; |
| int bestExactitude = 0; |
| for (O oper : operations) { |
| if (name.equals(uml.getName(oper))) { |
| int exactitude = matchArgsWithExactitude(owner, uml.getParameters(oper), args); |
| if (exactitude >= bestExactitude) { |
| if (exactitude > bestExactitude) { |
| if (matches != null) { |
| matches.clear(); |
| } |
| bestExactitude = exactitude; |
| } |
| if (matches == null) { |
| // assume a small number of redefinitions |
| matches = new java.util.ArrayList<O>(3); |
| } |
| |
| matches.add(oper); |
| } |
| } |
| } |
| return matches; |
| } |
| |
| public boolean isStandardLibraryFeature(C owner, Object feature) { |
| boolean result = owner instanceof PredefinedType<?>; |
| |
| if (result) { |
| result = uml.isOperation(feature) |
| ? !env.getAdditionalOperations(owner).contains(feature) |
| : !env.getAdditionalAttributes(owner).contains(feature); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Finds the most specific redefinition in a given list of features from a |
| * classifier hierarchy. |
| * |
| * @param features |
| * the definitions of a feature; must have at least one element |
| * |
| * @return the most specific redefinition of the list of features |
| * |
| * @throws IndexOutOfBoundsException |
| * if there is not at least one feature in the list |
| */ |
| private <F> F mostSpecificRedefinition(List<? extends F> features) { |
| |
| Map<C, F> redefinitions = new java.util.LinkedHashMap<C, F>(); |
| |
| for (F next : features) { |
| redefinitions.put(uml.getOwningClassifier(next), next); |
| } |
| |
| List<C> classifiers = new java.util.ArrayList<C>(redefinitions.keySet()); |
| |
| // remove all classifiers that are ancestors of another classifier |
| // in the map |
| outer : for (;;) { |
| for (C next : classifiers) { |
| if (classifiers.removeAll(uml.getAllSupertypes(next))) { |
| continue outer; // don't want a concurrent modification |
| } |
| } |
| |
| break outer; |
| } |
| |
| if ((classifiers.size() > 1) && ParsingOptions.getValue(env, ParsingOptions.OVERLOAD_AMBIGUITY_IS_INVALID)) { |
| return null; |
| } |
| // there will at least be one remaining |
| return redefinitions.get(classifiers.get(0)); |
| } |
| |
| /** |
| * The <tt>OclVoid</tt> and <tt>OclInvalid</tt> types are defined as |
| * conforming to all other types. Therefore, we can try a little harder to |
| * match certain operations that it is useful to support, such as |
| * <tt>{@literal =}</tt> and <tt>{@literal <>}</tt>. |
| * |
| * @param owner |
| * the classifier to search (void or invalid) |
| * @param name |
| * the name of the operation |
| * @param args |
| * a list of arguments to match against the operation signature, |
| * as either expressions or variables |
| * |
| * @return the matching operation, or <code>null</code> if not found |
| */ |
| private O findOperationForVoidOrInvalid(C owner, String name, |
| List<? extends TypedElement<C>> args) { |
| |
| O result = null; |
| |
| if (args.size() == 1) { |
| C argType = args.get(0).getType(); |
| |
| if (argType != owner) { |
| // let us search the type of the argument to determine whether |
| // we can find this operation |
| result = findOperationMatching(argType, name, args); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Convenience method invoking |
| * <code>getProblemHandler().utilityProblem</code> with a |
| * <code>ProblemHandler.errorSeverity</code>. |
| * |
| * @param problemMessage |
| * message describing the problem |
| * @param problemContext |
| * optional message describing the reporting context |
| * @param problemObject |
| * optional object associated with the problem |
| */ |
| private void error(String problemMessage, String problemContext, |
| Object problemObject) { |
| OCLUtil.getAdapter(env, BasicEnvironment.class).utilityError( |
| problemMessage, problemContext, problemObject); |
| } |
| |
| /** |
| * Null-safe alternative to {@link ENamedElement#getName()}. |
| * |
| * @param element |
| * a named element that may be <code>null</code> |
| * @return the element's name, or <code>null</code> if the element is |
| * <code>null</code> |
| */ |
| private String getName(Object element) { |
| return (element == null) |
| ? null |
| : uml.getName(element); |
| } |
| |
| /** |
| * Resolves the specified signature of tuple parts to a tuple type already |
| * available in the current environment, or else a new one. |
| * |
| * @param parts |
| * specification of the tuple type signature |
| * @return the resolved or new tuple type |
| */ |
| protected abstract TupleType<O, P> resolveTupleType( |
| EList<? extends TypedElement<C>> parts); |
| |
| /** |
| * Resolves the specified collection type to a collection type already |
| * available in the current environment, or else a new one. |
| * |
| * @param kind |
| * the kind of collection type to obtain |
| * @param elementType |
| * the element type of the requisite collection type |
| * |
| * @return the resolved or new collection type |
| */ |
| protected abstract CollectionType<C, O> resolveCollectionType( |
| CollectionKind kind, C elementType); |
| |
| /** |
| * Resolves the specified template of a demand-created type to one that is |
| * already defined in the current environment, or else a new one or even the |
| * given type (which would usually then be added to the environment). |
| * |
| * @param type |
| * the template/prototype/example of a type to resolve in the |
| * current environment |
| * @return the resolved type |
| */ |
| protected abstract C resolve(C type); |
| } |