| /******************************************************************************* |
| * Copyright (c) 2005, 2012 IBM Corporation 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 - Initial API and implementation |
| * Zeligsoft - Bugs 182994, 252600 |
| * SAP - Bug 339052 |
| *******************************************************************************/ |
| |
| package org.eclipse.ocl.ecore; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EAnnotation; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EParameter; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.ETypedElement; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.ocl.AbstractEnvironment; |
| import org.eclipse.ocl.AmbiguousLookupException; |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.EnvironmentFactory; |
| import org.eclipse.ocl.LookupException; |
| import org.eclipse.ocl.TypeResolver; |
| import org.eclipse.ocl.ecore.internal.EcoreForeignMethods; |
| import org.eclipse.ocl.ecore.internal.OCLFactoryImpl; |
| import org.eclipse.ocl.ecore.internal.OCLStandardLibraryImpl; |
| import org.eclipse.ocl.ecore.internal.TypeResolverImpl; |
| import org.eclipse.ocl.ecore.internal.UMLReflectionImpl; |
| import org.eclipse.ocl.ecore.opposites.OppositeEndFinder; |
| import org.eclipse.ocl.expressions.ExpressionsPackage; |
| import org.eclipse.ocl.expressions.Variable; |
| import org.eclipse.ocl.expressions.impl.ExpressionsPackageImpl; |
| import org.eclipse.ocl.internal.l10n.OCLMessages; |
| import org.eclipse.ocl.lpg.ProblemHandler; |
| import org.eclipse.ocl.options.ParsingOptions; |
| import org.eclipse.ocl.options.ProblemOption; |
| import org.eclipse.ocl.parser.AbstractOCLAnalyzer; |
| import org.eclipse.ocl.types.OCLStandardLibrary; |
| import org.eclipse.ocl.types.TypesPackage; |
| import org.eclipse.ocl.util.TypeUtil; |
| import org.eclipse.ocl.utilities.OCLFactory; |
| import org.eclipse.ocl.utilities.UMLReflection; |
| import org.eclipse.ocl.utilities.UtilitiesPackage; |
| |
| /** |
| * Implementation of the {@link Environment} for parsing OCL expressions on |
| * Ecore models. The <code>EcoreEnvironment</code> uses a client-supplied |
| * package registry (or the global registry) to look up {@link EPackage}s |
| * by qualified name. |
| * |
| * @author Edith Schonberg (edith) |
| * @author Christian W. Damus (cdamus) |
| */ |
| public class EcoreEnvironment |
| extends AbstractEnvironment< |
| EPackage, EClassifier, EOperation, EStructuralFeature, |
| EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, |
| EClass, EObject> |
| implements EnvironmentWithHiddenOpposites { |
| |
| /** |
| * The namespace URI of the Ecore representation of the OCL Standard Library. |
| * |
| * @since 1.3 |
| */ |
| public static final String OCL_STANDARD_LIBRARY_NS_URI = "http://www.eclipse.org/ocl/1.1.0/oclstdlib.ecore"; //$NON-NLS-1$ |
| |
| private static final Map<List<String>, EPackage> OCL_PACKAGES = |
| new java.util.HashMap<List<String>, EPackage>(); |
| |
| |
| static { |
| List<String> names = new java.util.ArrayList<String>(); |
| names.add(ExpressionsPackageImpl.OCL_ROOT_PACKAGE.getName()); |
| OCL_PACKAGES.put(names, ExpressionsPackageImpl.OCL_ROOT_PACKAGE); |
| |
| names = new java.util.ArrayList<String>(names); |
| names.add(ExpressionsPackage.eINSTANCE.getName()); |
| OCL_PACKAGES.put(names, ExpressionsPackage.eINSTANCE); |
| |
| names = new java.util.ArrayList<String>(names); |
| names.set(1, TypesPackage.eINSTANCE.getName()); |
| OCL_PACKAGES.put(names, TypesPackage.eINSTANCE); |
| |
| names = new java.util.ArrayList<String>(names); |
| names.set(1, UtilitiesPackage.eINSTANCE.getName()); |
| OCL_PACKAGES.put(names, UtilitiesPackage.eINSTANCE); |
| |
| names = new java.util.ArrayList<String>(names); |
| names.set(1, EcorePackage.eINSTANCE.getName()); |
| OCL_PACKAGES.put(names, EcorePackage.eINSTANCE); |
| } |
| |
| /** |
| * The registry for package lookups. |
| */ |
| private EPackage.Registry registry; |
| |
| private EnvironmentFactory< |
| EPackage, EClassifier, EOperation, EStructuralFeature, |
| EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, |
| EClass, EObject> |
| factory; |
| |
| private TypeResolver<EClassifier, EOperation, EStructuralFeature> typeResolver; |
| |
| private OppositeEndFinder oppositeEndFinder; |
| |
| /** |
| * Initializes me with a package registry for package look-ups. |
| * |
| * @param reg a package registry |
| * @deprecated Use {@link #EcoreEnvironment(EcoreEnvironmentFactory, Resource)} instead |
| */ |
| @Deprecated |
| protected EcoreEnvironment(EPackage.Registry reg) { |
| registry = reg; |
| typeResolver = createTypeResolver(); |
| } |
| |
| /** |
| * Initializes me with an environment factory from which package registry |
| * and opposite end finder (if any) are obtained consistently, and from a |
| * resource in which I am persisted (and from which I load myself if it |
| * already has content). |
| * |
| * @param fac |
| * a package registry |
| * @param resource |
| * a resource, which may or may not already have content |
| * @since 3.1 |
| */ |
| protected EcoreEnvironment(EcoreEnvironmentFactory fac, Resource resource) { |
| factory = fac; |
| registry = fac.getEPackageRegistry(); |
| oppositeEndFinder = fac.getOppositeEndFinder(); |
| typeResolver = createTypeResolver(resource); |
| } |
| |
| /** |
| * Initializes me with a package registry and a resource in which I am |
| * persisted (and from which I load myself if it already has content). |
| * |
| * @param reg a package registry |
| * @param resource a resource, which may or may not already have content |
| * @deprecated Use {@link #EcoreEnvironment(EcoreEnvironmentFactory, Resource)} instead |
| */ |
| @Deprecated |
| protected EcoreEnvironment(EPackage.Registry reg, Resource resource) { |
| registry = reg; |
| typeResolver = createTypeResolver(resource); |
| } |
| |
| /** |
| * Initializes me with a parent environment, from which I inherit such things |
| * as a package registry and a resource. |
| * |
| * @param parent my parent environment |
| */ |
| protected EcoreEnvironment( |
| Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> parent) { |
| |
| super((EcoreEnvironment) parent); |
| |
| EcoreEnvironment eparent = (EcoreEnvironment) parent; |
| |
| if (eparent != null) { |
| factory = eparent.factory; |
| registry = eparent.registry; |
| typeResolver = eparent.getTypeResolver(); |
| oppositeEndFinder = eparent.oppositeEndFinder; |
| } else { |
| registry = EPackage.Registry.INSTANCE; |
| typeResolver = createTypeResolver(); |
| } |
| } |
| |
| // implements the inherited specification |
| public EnvironmentFactory< |
| EPackage, EClassifier, EOperation, EStructuralFeature, |
| EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, |
| EClass, EObject> |
| getFactory() { |
| if (factory != null) { |
| return factory; |
| } |
| |
| if (getInternalParent() != null) { |
| factory = getInternalParent().getFactory(); |
| if (factory != null) { |
| return factory; |
| } |
| } |
| |
| // obtain a reasonable default factory |
| if (registry == EPackage.Registry.INSTANCE) { |
| factory = EcoreEnvironmentFactory.INSTANCE; |
| } else { |
| factory = new EcoreEnvironmentFactory(registry); |
| } |
| |
| return factory; |
| } |
| |
| /** |
| * Sets the factory that created me. This method should only be invoked by |
| * that factory. If the factory is an {@link EcoreEnvironmentFactory}, its |
| * {@link EcoreEnvironmentFactory#getOppositeEndFinder() opposite end |
| * finder} will be used as this environment's {@link #oppositeEndFinder |
| * opposite end finder}. |
| * |
| * @param factory |
| * my originating factory |
| * @deprecated {@link #factory} will become final in future releases; use |
| * one of the constructors taking an |
| * {@link EcoreEnvironmentFactory} argument instead |
| */ |
| @Deprecated |
| protected void setFactory(EnvironmentFactory< |
| EPackage, EClassifier, EOperation, EStructuralFeature, |
| EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, |
| EClass, EObject> factory) { |
| this.factory = factory; |
| if (factory instanceof EcoreEnvironmentFactory) { |
| oppositeEndFinder = ((EcoreEnvironmentFactory)factory).getOppositeEndFinder(); |
| } |
| } |
| |
| // implements the inherited specification |
| public void setParent(Environment< |
| EPackage, EClassifier, EOperation, EStructuralFeature, |
| EEnumLiteral, EParameter, |
| EObject, CallOperationAction, SendSignalAction, Constraint, |
| EClass, EObject> env) { |
| super.setParent((EcoreEnvironment) env); |
| } |
| |
| // implements the inherited specification |
| public OCLStandardLibrary<EClassifier> getOCLStandardLibrary() { |
| return OCLStandardLibraryImpl.INSTANCE; |
| } |
| |
| // implements the inherited specification |
| public TypeResolver<EClassifier, EOperation, EStructuralFeature> getTypeResolver() { |
| return typeResolver; |
| } |
| |
| // implements the inherited specification |
| public OCLFactory getOCLFactory() { |
| return OCLFactoryImpl.INSTANCE; |
| } |
| |
| // implements the inherited specification |
| public UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> getUMLReflection() { |
| return UMLReflectionImpl.INSTANCE; |
| } |
| |
| /** |
| * Creates a new type resolver for use with this environment, persisted |
| * in a default resource. |
| * |
| * @return a new type resolver |
| * |
| * @deprecated Override the {@link #createTypeResolver(Resource)} method, |
| * instead, handling the case where the resource is <code>null</code> |
| */ |
| @Deprecated |
| protected TypeResolver<EClassifier, EOperation, EStructuralFeature> createTypeResolver() { |
| return createTypeResolver(null); |
| } |
| |
| /** |
| * <p> |
| * Creates a new type resolver for use with this environment. |
| * </p><p> |
| * Subclasses may override. |
| * </p> |
| * |
| * @param resource the resource for the type resolver's persistence |
| * @return a new type resolver |
| * |
| * @since 1.2 |
| */ |
| protected TypeResolver<EClassifier, EOperation, EStructuralFeature> createTypeResolver(Resource resource) { |
| return new TypeResolverImpl(this, resource); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * Implements the inherited specification by looking up the qualified name |
| * in my package registry. |
| * </p> |
| */ |
| public EPackage lookupPackage(List<String> path) { |
| if (!path.isEmpty() && OCL_PACKAGES.containsKey(path)) { |
| return OCL_PACKAGES.get(path); |
| } |
| |
| EPackage pkg = null; |
| EPackage currPkg = getContextPackage(); |
| |
| // Check whether this package is in the default package |
| if (currPkg != null) { |
| List<String> lookup = path; |
| |
| while (currPkg != null) { |
| pkg = currPkg; |
| |
| for (int i = 0; i < lookup.size(); i++) { |
| String name = lookup.get(i); |
| pkg = EcoreForeignMethods.getESubpackage(pkg, name); |
| |
| if (pkg == null) { |
| break; |
| } |
| } |
| |
| if (pkg != null) { |
| return pkg; |
| } |
| |
| if ((currPkg == getContextPackage()) && (lookup.size() > 0) |
| && EcoreForeignMethods.isNamed(lookup.get(0), currPkg)) { |
| // handle the case where the first part of the qualified |
| // name matches the context package name |
| lookup = lookup.subList(1, lookup.size()); |
| } else { |
| lookup = path; |
| currPkg = currPkg.getESuperPackage(); |
| } |
| } |
| } |
| |
| // Check whether this package exists in the global package registry |
| return findPackageWithStrategy(path, registry); |
| } |
| |
| // implements the inherited specification |
| public EClassifier lookupClassifier(List<String> names) { |
| EPackage pkg = null; |
| EPackage currPkg = getContextPackage(); |
| |
| if (names.size() > 1) { |
| List<String> lookup = names; |
| |
| // Check whether this package is in the default package |
| if (currPkg != null) { |
| while (currPkg != null) { |
| |
| pkg = currPkg; |
| for (int i = 0; i < lookup.size() - 1; i++) { |
| String name = lookup.get(i); |
| pkg = EcoreForeignMethods.getESubpackage(pkg, name); |
| |
| if (pkg == null) { |
| break; |
| } |
| } |
| |
| if (pkg != null) { |
| return EcoreForeignMethods.getEClassifier(pkg, lookup |
| .get(lookup.size() - 1)); |
| } |
| |
| if ((currPkg == getContextPackage()) && (lookup.size() > 1) |
| && EcoreForeignMethods.isNamed(lookup.get(0), currPkg)) { |
| // handle the case where the first part of the qualified |
| // name matches the context package name |
| lookup = lookup.subList(1, lookup.size()); |
| } else { |
| lookup = names; |
| currPkg = currPkg.getESuperPackage(); |
| } |
| } |
| } |
| |
| // Check whether this package exists |
| List<String> newNames = names.subList(0, names.size() - 1); |
| pkg = findPackageWithStrategy(newNames, registry); |
| if (pkg == null) { |
| return null; |
| } |
| |
| return EcoreForeignMethods.getEClassifier(pkg, names.get(names.size() - 1)); |
| } else if (getContextPackage() != null) { |
| String name = names.get(0); |
| EClassifier result = null; |
| while (currPkg != null) { |
| result = EcoreForeignMethods.getEClassifier(currPkg, name); |
| |
| if (result != null) { |
| return result; |
| } |
| |
| currPkg = currPkg.getESuperPackage(); |
| } |
| } |
| |
| return null; |
| } |
| |
| private EPackage findPackageWithStrategy(List<String> newNames, EPackage.Registry registry) { |
| EPackage pkg; |
| switch (ParsingOptions.getValue(this, ParsingOptions.PACKAGE_LOOKUP_STRATEGY)) { |
| case LOOKUP_PACKAGE_BY_ALIAS: |
| pkg = findPackageByAlias(newNames, registry); |
| break; |
| case LOOKUP_PACKAGE_BY_ALIAS_THEN_NAME: |
| pkg = findPackageByAlias(newNames, registry); |
| if (pkg == null) { |
| pkg = findPackage(newNames, registry); |
| } |
| break; |
| case LOOKUP_PACKAGE_BY_NAME: |
| pkg = findPackage(newNames, registry); |
| break; |
| default: |
| throw new RuntimeException("Unknown PACKAGE_LOOKUP_STRATEGY value "+ //$NON-NLS-1$ |
| ParsingOptions.getValue(this, ParsingOptions.PACKAGE_LOOKUP_STRATEGY)); |
| } |
| return pkg; |
| } |
| |
| /** |
| * Obtains the states matching the specified path prefix in the owner type |
| * by trying the {@link #collectStates} method on it and, recursively, its |
| * supertypes to find all matches. For implicit (<code>null</code>) owners, |
| * looks up the innermost-scoped variable as the implicit source and tries |
| * again on this variable's type. |
| * <p> |
| * To extend this implementation, override the |
| * {@link #collectStates} method. |
| * </p> |
| */ |
| public List<EObject> getStates(EClassifier owner, List<String> pathPrefix) { |
| EList<EObject> result = new BasicEList<EObject>(); |
| |
| collectStates(owner, pathPrefix, result); |
| |
| if (owner instanceof EClass) { |
| // search supertypes |
| for (EClass superclass : ((EClass) owner).getEAllSuperTypes()) { |
| collectStates(superclass, pathPrefix, result); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implemented by subclasses to find all states in the specified owner type |
| * that match the given path name prefix and add them to the accumulator |
| * list. The default implementation does nothing, as Ecore does not model |
| * states. |
| * <p> |
| * Implementors must only provide the states defined directly in the |
| * namespace indicated by the path prefix (i.e., only one level). |
| * </p> |
| * |
| * @param owner the owner type |
| * @param pathPrefix partial qualified name, specifying the parent of the |
| * states to be collection |
| * @param states a list of states directly owned by the namespace indicated |
| * by path prefix, within the owner type |
| * |
| * @see #getStates(EClassifier, List) |
| */ |
| protected void collectStates(EClassifier owner, List<String> pathPrefix, List<EObject> states) { |
| // do nothing |
| } |
| |
| // implements the inherited specification |
| public EStructuralFeature defineAttribute( |
| EClassifier owner, |
| org.eclipse.ocl.expressions.Variable< |
| EClassifier, EParameter> variable, |
| Constraint constraint) { |
| resetTypeCaches(); |
| EStructuralFeature result; |
| |
| String name = variable.getName(); |
| EClassifier type = variable.getType(); |
| |
| if (type instanceof EClass) { |
| result = EcoreFactory.eINSTANCE.createEReference(); |
| } else { |
| result = EcoreFactory.eINSTANCE.createEAttribute(); |
| } |
| |
| result.setName(name); |
| result.setEType(type); |
| |
| Constraint existing = getDefinition(result); |
| if (existing != null) { |
| // replace existing definition |
| EcoreUtil.replace(existing, constraint); |
| } else { |
| EAnnotation ann = result.getEAnnotation(Environment.OCL_NAMESPACE_URI); |
| if (ann == null) { |
| ann = EcoreFactory.eINSTANCE.createEAnnotation(); |
| ann.setSource(Environment.OCL_NAMESPACE_URI); |
| result.getEAnnotations().add(ann); |
| } |
| |
| ann.getContents().add(constraint); |
| } |
| |
| addHelperProperty(owner, result); |
| |
| return result; |
| } |
| |
| // implements the inherited specification |
| public EOperation defineOperation(EClassifier owner, String name, |
| EClassifier type, |
| List<org.eclipse.ocl.expressions.Variable< |
| EClassifier, EParameter>> params, |
| Constraint constraint) { |
| resetTypeCaches(); |
| EOperation result = EcoreFactory.eINSTANCE.createEOperation(); |
| |
| result.setName(name); |
| result.setEType((type == null) ? getOCLStandardLibrary().getOclVoid() : |
| type); |
| |
| for (Variable<EClassifier, EParameter> next : params) { |
| EParameter param = EcoreFactory.eINSTANCE.createEParameter(); |
| param.setName(next.getName()); |
| param.setEType((next.getType() == null)? |
| getOCLStandardLibrary().getOclVoid() : next.getType()); |
| |
| result.getEParameters().add(param); |
| } |
| |
| Constraint existing = getDefinition(result); |
| if (existing != null) { |
| // replace existing definition |
| EcoreUtil.replace(existing, constraint); |
| } else { |
| EAnnotation ann = result.getEAnnotation(Environment.OCL_NAMESPACE_URI); |
| if (ann == null) { |
| ann = EcoreFactory.eINSTANCE.createEAnnotation(); |
| ann.setSource(Environment.OCL_NAMESPACE_URI); |
| result.getEAnnotations().add(ann); |
| } |
| |
| ann.getContents().add(constraint); |
| } |
| |
| addHelperOperation(owner, result); |
| |
| return result; |
| } |
| |
| // implements the inherited specification |
| public void undefine(Object feature) { |
| Constraint definition = getDefinition(feature); |
| |
| if (definition == null) { |
| throw new IllegalArgumentException( |
| "not an additional feature: " + feature); //$NON-NLS-1$ |
| } |
| |
| EcoreUtil.remove((EObject) feature); |
| EcoreUtil.remove(definition); |
| |
| definition.getConstrainedElements().clear(); |
| resetTypeCaches(); |
| } |
| |
| public Constraint getDefinition(Object feature) { |
| Constraint result = null; |
| ETypedElement typedFeature = (ETypedElement) feature; |
| |
| EAnnotation ann = typedFeature.getEAnnotation( |
| Environment.OCL_NAMESPACE_URI); |
| |
| if ((ann != null) && !ann.getContents().isEmpty()) { |
| for (EObject o : ann.getContents()) { |
| if ((o instanceof Constraint) |
| && UMLReflection.DEFINITION.equals(((Constraint) o).getStereotype())) { |
| |
| result = (Constraint) o; |
| break; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Looks in the EMF registry for a package with the specified qualified |
| * package name. Uses the global package registry. |
| * |
| * @param packageNames |
| * the qualified package name |
| * @return the matching EPackage, or <code>null</code> if not found |
| */ |
| static public EPackage findPackage(List<String> packageNames) { |
| return findPackage(packageNames, EPackage.Registry.INSTANCE); |
| } |
| |
| /** |
| * Looks in the given registry for a package with the specified qualified |
| * package name. |
| * |
| * @param packageNames |
| * the qualified package name |
| * @param registry the EPackage.Registry to look in |
| * @return the matching EPackage, or <code>null</code> if not found |
| */ |
| static public EPackage findPackage(List<String> packageNames, EPackage.Registry registry) { |
| if (packageNames.isEmpty()) { |
| return null; |
| } |
| |
| if (OCL_PACKAGES.containsKey(packageNames)) { |
| return OCL_PACKAGES.get(packageNames); |
| } |
| |
| String name = packageNames.get(0); |
| for (Object next : registry.values()) { |
| if (next instanceof EPackage) { |
| EPackage ePackage = (EPackage) next; |
| |
| // only consider root-level packages when searching by name |
| if ((ePackage.getESuperPackage() == null) |
| && EcoreForeignMethods.isNamed(name, ePackage)) { |
| |
| EPackage tentativeResult = findNestedPackage( |
| packageNames.subList(1, packageNames.size()), |
| ePackage); |
| |
| if (tentativeResult != null) { |
| return tentativeResult; |
| } |
| } |
| } |
| } |
| |
| return findPackageByNSPrefix(packageNames, registry); |
| } |
| |
| /** |
| * Looks in the given registry for an 'nsURI' matching the first element in <tt>packageNames</tt>. |
| * If found, further elements of <tt>packageNames</tt> identify nested packages. |
| * <t> |
| * This search supports the {@link ParsingOptions.LOOKUP_PACKAGE_BY_ALIAS} strategy. |
| * |
| * @param packageNames |
| * the qualified package name |
| * @param registry |
| * the EPackage.Registry to look in |
| * @return the matching EPackage, or <code>null</code> if not found |
| */ |
| static private EPackage findPackageByAlias(List<String> packageNames, EPackage.Registry registry) { |
| if (packageNames.isEmpty()) { |
| return null; |
| } |
| String name = packageNames.get(0); |
| EPackage ePackage = registry.getEPackage(name); |
| if (ePackage != null) { |
| List<String> packageSubList = packageNames.subList(1, packageNames.size()); |
| ePackage = findNestedPackage(packageSubList, ePackage); |
| } |
| return ePackage; |
| } |
| |
| /** |
| * Looks in the given package for a nested package with the specified relative |
| * package name. |
| * |
| * @param packageNames |
| * the relativ package name |
| * @param epackage the starting package to look in |
| * @return the matching EPackage, or <code>null</code> if not found |
| */ |
| private static EPackage findNestedPackage( |
| List<String> packageNames, |
| EPackage epackage) { |
| |
| EPackage result = epackage; |
| |
| for (String name : packageNames) { |
| result = EcoreForeignMethods.getESubpackage(result, name); |
| |
| if (result == null) { |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Looks in the given registry for a package with the specified qualified |
| * package name, matching the name against the namespace prefixes of the |
| * packages in the registry. |
| * |
| * @param packageNames |
| * the qualified package name |
| * @param registry the EPackage.Registry to look in |
| * @return the NSPrefix-matching EPackage, or <code>null</code> if not found |
| */ |
| private static EPackage findPackageByNSPrefix( |
| List<String> packageNames, |
| EPackage.Registry registry) { |
| |
| StringBuilder stringBuffer = new StringBuilder(); |
| Iterator<String> it = packageNames.iterator(); |
| while (it.hasNext()) { |
| stringBuffer.append(it.next()); |
| if (it.hasNext()) { |
| stringBuffer.append(".");//$NON-NLS-1$ |
| } |
| } |
| |
| String nsPrefix = stringBuffer.toString(); |
| |
| for (Object next : registry.values()) { |
| if (next instanceof EPackage) { |
| EPackage ePackage = (EPackage) next; |
| if (nsPrefix.equals(ePackage.getNsPrefix())) { |
| return ePackage; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| // implements the inherited specification |
| public boolean isInPostcondition( |
| org.eclipse.ocl.expressions.OCLExpression<EClassifier> exp) { |
| |
| Constraint constraint = null; |
| EObject parent = exp; |
| while (parent != null) { |
| if (parent instanceof Constraint) { |
| constraint = (Constraint) parent; |
| break; |
| } |
| |
| parent = parent.eContainer(); |
| } |
| |
| return (constraint != null) |
| && UMLReflection.POSTCONDITION.equals(constraint.getStereotype()); |
| } |
| |
| // implements the interface method |
| /** |
| * @since 3.1 |
| */ |
| public Variable<EClassifier, EParameter> lookupImplicitSourceForOppositeProperty(String name) { |
| Variable<EClassifier, EParameter> vdcl; |
| |
| for (int i = getElementsSize() - 1; i >= 0; i--) { |
| VariableEntry element = getElement(i); |
| vdcl = element.getVariable(); |
| EClassifier owner = vdcl.getType(); |
| |
| if (!element.isExplicit() && (owner != null)) { |
| EReference property = safeTryLookupOppositeProperty(owner, name); |
| if (property != null) { |
| return vdcl; |
| } |
| } |
| |
| } |
| |
| // try the "self" variable, last |
| vdcl = getSelfVariable(); |
| if (vdcl != null) { |
| EClassifier owner = vdcl.getType(); |
| if (owner != null) { |
| EReference property = safeTryLookupOppositeProperty(owner, name); |
| if (property != null) { |
| return vdcl; |
| } |
| } |
| } |
| |
| return null; |
| |
| } |
| |
| /** |
| * Looks up a non-navigable association end on behalf of |
| * the specified <code>owner</code> classifier (which is at that end). |
| * |
| * @param owner |
| * a classifier in the context of which the property is used |
| * @param name |
| * the end name to look up |
| * |
| * @return the non-navigable end, or <code>null</code> if it cannot |
| * be found |
| * |
| * @throws LookupException in case that multiple non-navigable properties |
| * are found that have the same name and the problem option is ERROR |
| * or worse |
| * @since 3.1 |
| */ |
| public EReference lookupOppositeProperty(EClassifier owner, String name) throws LookupException { |
| if (owner == null) { |
| Variable<EClassifier, EParameter> vdcl = lookupImplicitSourceForOppositeProperty(name); |
| |
| if (vdcl == null) { |
| return null; |
| } |
| |
| owner = vdcl.getType(); |
| } |
| |
| List<EReference> matches = new java.util.ArrayList<EReference>(2); |
| findOppositeEnds(owner, name, matches); |
| |
| if (matches.isEmpty()) { |
| return null; |
| } else if (matches.size() > 1) { |
| // ambiguous matches. What to do? |
| if (notOK(ProblemOption.AMBIGUOUS_ASSOCIATION_ENDS)) { |
| ProblemHandler.Severity sev = getValue(ProblemOption.AMBIGUOUS_ASSOCIATION_ENDS); |
| |
| // will have to report the problem |
| String message = OCLMessages.bind(OCLMessages.Ambig_AssocEnd_, |
| name, getUMLReflection().getName(owner)); |
| |
| if (sev.getDiagnosticSeverity() >= Diagnostic.ERROR) { |
| throw new AmbiguousLookupException(message, matches); |
| } else { |
| getProblemHandler().analyzerProblem(sev, message, |
| "lookupNonNavigableProperty", -1, -1); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| return matches.get(0); |
| } |
| |
| /** |
| * This default implementation simply delegates to the |
| * {@link #lookupOppositeProperty(EClassifier, String)} method. |
| * |
| * @since 3.1 |
| */ |
| public EReference tryLookupOppositeProperty(EClassifier owner, String name) |
| throws LookupException { |
| EReference result = lookupOppositeProperty(owner, name); |
| if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) { |
| result = lookupOppositeProperty(owner, AbstractOCLAnalyzer.unescape(name)); |
| } |
| return result; |
| } |
| |
| /** |
| * This default implementation simply delegates to the |
| * {@link Environment#lookupProperty(Object, String)} method. |
| * @since 3.1 |
| */ |
| public EStructuralFeature tryLookupProperty(EClassifier owner, String name) |
| throws LookupException { |
| |
| EStructuralFeature result = lookupProperty(owner, name); |
| // look up non-navigable/unnamed ends in any case because they may be located in |
| // a specialization of result's owner, hence take precedence over |
| // result: |
| EReference nonNavigableEnd = (EReference) lookupNonNavigableEnd(owner, name); |
| if ((nonNavigableEnd == null) && AbstractOCLAnalyzer.isEscaped(name)) { |
| nonNavigableEnd = (EReference) lookupNonNavigableEnd(owner, |
| AbstractOCLAnalyzer.unescape(name)); |
| } |
| if (result != null) { |
| // Ambiguous hidden opposite ends may have been found. |
| // Don't consider unnamed opposite ends if a named "real" end has |
| // already been found |
| if (nonNavigableEnd != null && nonNavigableEnd.getName() != null) { |
| EReference nonNavigableEndOpposite = nonNavigableEnd.getEOpposite(); |
| // check for ambiguity; note that nonNavigableEnd may be a |
| // temporary property which doesn't have a container set; type |
| // therefore needs |
| // to be determined through opposite |
| EClassifier nonNavigableEndOwner = TypeUtil.getPropertyType(this, null, |
| nonNavigableEndOpposite); |
| if (getUMLReflection().getAllSupertypes(nonNavigableEndOwner) |
| .contains(getUMLReflection().getOwningClassifier(result))) { |
| result = nonNavigableEnd; |
| } else if (!getUMLReflection().getAllSupertypes( |
| getUMLReflection().getOwningClassifier(result)).contains( |
| TypeUtil.getPropertyType(this, null, nonNavigableEnd.getEOpposite()))) { |
| ProblemHandler.Severity sev = getValue(ProblemOption.AMBIGUOUS_ASSOCIATION_ENDS); |
| // will have to report the problem |
| String message = OCLMessages.bind( |
| OCLMessages.Ambig_AssocEnd_, name, getUMLReflection() |
| .getName(owner)); |
| if (sev.getDiagnosticSeverity() >= Diagnostic.ERROR) { |
| List<EStructuralFeature> ambiguousMatches = new ArrayList<EStructuralFeature>(); |
| ambiguousMatches.add(result); |
| ambiguousMatches.add(nonNavigableEnd); |
| throw new AmbiguousLookupException(message, |
| ambiguousMatches); |
| } else { |
| getProblemHandler().analyzerProblem(sev, message, |
| "lookupNonNavigableProperty", -1, -1); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| if (result == null) { |
| result = nonNavigableEnd; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Wrapper for the "try" operation that doesn't throw, but just returns the |
| * first ambiguous match in case of ambiguity. |
| */ |
| private EReference safeTryLookupOppositeProperty(EClassifier owner, String name) { |
| EReference result = null; |
| try { |
| result = lookupOppositeProperty(owner, name); |
| if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) { |
| result = lookupOppositeProperty(owner, AbstractOCLAnalyzer.unescape(name)); |
| } |
| } catch (LookupException e) { |
| if (!e.getAmbiguousMatches().isEmpty()) { |
| result = (EReference) e.getAmbiguousMatches().get(0); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Searches for non-navigable association ends with the specified |
| * <tt>name</tt> at the given <tt>classifier</tt>'s end of an association. |
| * Subclasses should reimplement this method if they support non-navigable |
| * association ends. |
| * |
| * @param classifier a classifier at an association end |
| * @param name the non-navigable end name to look for |
| * @param ends collects the ends found by the subclass implementation |
| * @since 3.1 |
| */ |
| protected void findOppositeEnds(EClassifier classifier, String name, |
| List<EReference> ends) { |
| if (oppositeEndFinder != null) { |
| oppositeEndFinder.findOppositeEnds(classifier, name, ends); |
| } |
| } |
| |
| /** |
| * If a "hidden" opposite is found (because a forward reference has a "Property.oppositeRoleName" |
| * annotation specifying a name for the opposite role), this method adds a temporary |
| * {@link EReference} to the <code>ends</code> list which has no container set and as its |
| * opposite uses the forward reference. The name of this temporary reference is set to |
| * <code>name</code> which equals the value of the "Property.oppositeRoleName" annotation |
| * on the forward reference.<p> |
| * |
| * The temporary reference is not a valid reference for at least two reasons: its container |
| * is not set and its opposite does not name it as its opposite. |
| * @since 3.1 |
| */ |
| @Override |
| protected void findNonNavigableAssociationEnds(EClassifier classifier, |
| String name, List<EStructuralFeature> ends) { |
| List<EReference> hiddenOpposites = new ArrayList<EReference>(); |
| if (oppositeEndFinder != null) { |
| oppositeEndFinder.findOppositeEnds(classifier, name, hiddenOpposites); |
| } |
| for (EReference forwardRef : hiddenOpposites) { |
| EReference temporaryOppositeRef = EcoreFactory.eINSTANCE.createEReference(); |
| temporaryOppositeRef.setEOpposite(forwardRef); |
| temporaryOppositeRef.setName(name); |
| temporaryOppositeRef.setEType(forwardRef.getEContainingClass()); |
| ends.add(temporaryOppositeRef); |
| } |
| } |
| |
| /** |
| * @since 3.1 |
| */ |
| public EClassifier getOppositePropertyType(EClassifier owner, |
| EReference property) { |
| return ((UMLReflectionImpl) getUMLReflection()).getOCLCollectionType( |
| property.getEContainingClass(), |
| /* ordered */false, /* unique */false); |
| } |
| |
| /** |
| * @since 3.1 |
| */ |
| public Map<String, EReference> getHiddenOppositeProperties( |
| EClassifier classifier) { |
| Map<String, EReference> result; |
| if (oppositeEndFinder == null) { |
| result = Collections.emptyMap(); |
| } else { |
| result = oppositeEndFinder.getAllOppositeEnds(classifier); |
| } |
| return result; |
| } |
| |
| /** |
| * @since 3.1 |
| */ |
| public OppositeEndFinder getOppositeEndFinder() { |
| return oppositeEndFinder; |
| } |
| } |