org.eclipse.ocl.AbstractEnvironment Maven / Gradle / Ivy
/**
*
*
* Copyright (c) 2006, 2008 IBM Corporation, Zeligsoft Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* E.D.Willink - Refactoring to support extensibility and flexible error handling
* Zeligsoft - Bugs 243079, 244948, 244886, 246469, 233673
* Adolfo Sanchez-Barbudo Herrera - Bug 234354, 233673
*
*
*
* $Id: AbstractEnvironment.java,v 1.21 2010/12/15 17:33:43 ewillink Exp $
*/
package org.eclipse.ocl;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.lpg.AbstractBasicEnvironment;
import org.eclipse.ocl.lpg.ProblemHandler;
import org.eclipse.ocl.options.Option;
import org.eclipse.ocl.options.ProblemOption;
import org.eclipse.ocl.parser.AbstractOCLAnalyzer;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.TupleType;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.util.UnicodeSupport;
import org.eclipse.ocl.utilities.PredefinedType;
import org.eclipse.ocl.utilities.TypedElement;
/**
* A partial implementation of the {@link Environment} interface providing
* some useful common behavior for providers of metamodel bindings. It is
* recommended to extend this class rather than to implement the
* Environment
interface from scratch.
*
* In particular, this class provides:
*
*
* - maintenance of the context package, classifier, operation, and property
* - tracking of variable declarations
* - lookup of operations, attributes, etc. by delegation to the other
* model introspection methods
* - determination of implicit source variables for operation calls,
* attribute navigations, etc.
* - tracking of operation bodies and attribute initializers/derivations
* - storage of additional attributes and operations
*
*
* along with some subclass hook methods and convenience methods.
*
*
* See the {@link Environment} class for a description of the
* generic type parameters of this class.
*
*
* @author Christian W. Damus (cdamus)
*/
public abstract class AbstractEnvironment
extends AbstractBasicEnvironment
implements Environment.Internal,
Environment.Lookup {
/* Used to generate implicit iterator variables */
private int generatorInt = 0;
private PK contextPackage;
private O contextOperation;
private P contextProperty;
/*
* List of declared variables and implicit variables, including "self".
* Implicit variables are generated when there is an iterator without any
* iteration variable specified.
*/
private List namedElements = new java.util.ArrayList();
private Variable selfVariable;
// map of operations to body conditions
private Map operationBodies = new java.util.HashMap();
// map of attributes to initial-value expressions
private Map propertyInitializers = new java.util.HashMap
();
// map of attributes to derivation expressions
private Map
propertyDerivations = new java.util.HashMap
();
private TypeChecker typeChecker;
/**
* Initializes me without a parent environment.
*/
protected AbstractEnvironment() {
super(null);
}
/**
* Initializes me with the specified parent environment.
*
* @param parent an environment (or null
)
*/
protected AbstractEnvironment(
Environment.Internal parent) {
super(parent);
}
/**
* Initializes me with the specified parent environment, which should be
* of the same type as me.
*
* @param parent an environment of the same type as me (or null
)
*/
protected AbstractEnvironment(
AbstractEnvironment parent) {
super(parent);
}
/**
* Generates a new, unique name for an implicit iterator variable.
*
* @return the new name
*/
private String generateName() {
generatorInt++;
return "temp" + generatorInt;//$NON-NLS-1$
}
// implements the interface method
@SuppressWarnings("unchecked")
public Environment.Internal getInternalParent() {
return (Environment.Internal) super.getParent();
}
// implements the interface method
/**
* @deprecated Since 1.2, use the {@link #getInternalParent()} method, instead.
*/
@SuppressWarnings("unchecked")
@Override
@Deprecated
public AbstractEnvironment getParent() {
return (AbstractEnvironment) super.getParent();
}
/**
* Assigns me a parent environment after construction. It is not advisable
* to set the parent to null
if I previously had one.
*
* @param parent my new parent
*/
public void setInternalParent(Environment.Internal parent) {
super.setParent(parent);
}
/**
* Assigns me a parent environment after construction. It is not advisable
* to set the parent to null
if I previously had one.
*
* @param parent my new parent
*/
protected void setParent(AbstractEnvironment parent) {
super.setParent(parent);
}
// implements the interface method
public PK getContextPackage() {
if (contextPackage != null) {
return contextPackage;
} else if (getInternalParent() != null) {
return getInternalParent().getContextPackage();
}
return null;
}
/**
* Assigns my context package.
*
* @param contextPackage my new context package
*/
protected void setContextPackage(PK contextPackage) {
this.contextPackage = contextPackage;
}
// implements the interface method
public C getContextClassifier() {
return getSelfVariable().getType();
}
// implements the interface method
public O getContextOperation() {
if (contextOperation != null) {
return contextOperation;
} else if (getInternalParent() != null) {
return getInternalParent().getContextOperation();
}
return null;
}
/**
* Assigns my context operation. This method does not create the
* variables for parameters and the return result.
*
* @param contextOperation my context operation
*/
protected void setContextOperation(O contextOperation) {
this.contextOperation = contextOperation;
}
// implements the interface method
public P getContextProperty() {
if (contextProperty != null) {
return contextProperty;
} else if (getInternalParent() != null) {
return getInternalParent().getContextProperty();
}
return null;
}
/**
* Assigns my context property.
*
* @param contextProperty my context property
*/
protected void setContextProperty(P contextProperty) {
this.contextProperty = contextProperty;
}
/**
* Obtains the resource in which my
* {@linkplain Environment#getTypeResolver() type resolver} persists
* OCL-generated types and additional features.
*
* @return my resource
*/
protected Resource getResource() {
return getTypeResolver().getResource();
}
// implements the interface method
public boolean isEmpty() {
return namedElements.isEmpty();
}
// implements the interface method
public Collection> getVariables() {
Collection> result = new java.util.ArrayList>();
for (int i = 0; i < namedElements.size(); i++) {
VariableEntry elem = namedElements.get(i);
if (elem.isExplicit) {
result.add(elem.variable);
}
}
if (getInternalParent() != null) {
// add all non-shadowed parent variables
for (Variable parentVar : getInternalParent().getVariables()) {
if (lookupLocal(parentVar.getName()) == null) {
result.add(parentVar);
}
}
}
return result;
}
// implements the interface method
public boolean addElement(String name, Variable elem, boolean isExplicit) {
if (name == null) {
name = generateName();
while (lookup(name) != null) {
name = generateName();
}
} else if (lookupLocal(name) != null) {
return false;
}
getUMLReflection().setName(elem, name);
VariableEntry newelem = new VariableEntry(name, elem, isExplicit);
namedElements.add(newelem);
addedVariable(name, elem, isExplicit);
return true;
}
/**
* Persists the specified variable in my resource. Subclasses may extend
* this method to perform other actions following the addition of a variable
* to the environment.
*
* @param name the variable name
* @param variable the variable added
* @param isExplicit whether it is an explicit or implicit variable
*/
protected void addedVariable(String name, Variable variable, boolean isExplicit) {
getResource().getContents().add(variable);
}
// implements the interface method
public void deleteElement(String name) {
for (Iterator iter = namedElements.iterator(); iter.hasNext();) {
VariableEntry elem = iter.next();
if (elem.name.equals(name)) {
iter.remove();
removedVariable(name, elem.variable, elem.isExplicit);
}
}
}
/**
* Removes the specified variable from my resource. Subclasses may extend
* this method to perform other actions following the removal of a variable
* from the environment.
*
* @param name the variable name
* @param variable the variable removed
* @param isExplicit whether it was an explicit or implicit variable
*/
protected void removedVariable(String name, Variable variable, boolean isExplicit) {
getResource().getContents().remove(variable);
}
// implements the interface method
public void setSelfVariable(Variable var) {
selfVariable = var;
// ensure that the environment knows its package context
if (getContextPackage() == null) {
C contextClassifier = getContextClassifier();
if (contextClassifier != null) {
setContextPackage(getUMLReflection().getPackage(contextClassifier));
}
}
}
// implements the interface method
public Variable getSelfVariable() {
Variable result = selfVariable;
if ((result == null) && (getInternalParent() != null)) {
result = getInternalParent().getSelfVariable();
}
return result;
}
/**
* @since 3.1
*/
protected int getElementsSize() {
return namedElements.size();
}
/**
* @since 3.1
*/
protected VariableEntry getElement(int index) {
return namedElements.get(index);
}
@SuppressWarnings("deprecation")
public void addHelperProperty(C owner, P property) {
addProperty(owner, property);
}
/**
* Allows subclasses to add a newly OCL-defined additional property to
* the environment. This should be called by the subclass's implementation
* of the {@link Environment#defineAttribute} method.
*
* @param owner the classifier in which context the attribute is defined
* @param property the additional attribute
*
* @deprecated Since 1.2, use the
* {@link Environment.Internal#addHelperProperty(Object, Object)}
* API, instead
*/
@Deprecated
protected void addProperty(C owner, P property) {
if (getInternalParent() != null) {
// propagate additional properties as high as possible so that they
// will be accessible to all child environments of the root
getInternalParent().addHelperProperty(owner, property);
} else {
getTypeResolver().resolveAdditionalAttribute(owner, property);
}
}
public List getAdditionalAttributes(C classifier) {
if (getInternalParent() != null) {
return getInternalParent().getAdditionalAttributes(classifier);
}
List
result = null;
TypeResolver res = getTypeResolver();
List additionals = res.getAdditionalAttributes(classifier);
if (!additionals.isEmpty()) {
result = new java.util.ArrayList
(additionals);
}
Collection extends C> allParents = (classifier instanceof PredefinedType>)
? OCLStandardLibraryUtil.getAllSupertypes(this,
(PredefinedType>) classifier)
: getUMLReflection().getAllSupertypes(classifier);
for (C general : allParents) {
additionals = res.getAdditionalAttributes(general);
if (!additionals.isEmpty()) {
if (result == null) {
result = new java.util.ArrayList
(additionals);
} else {
result.addAll(additionals);
}
}
}
if (result == null) {
result = Collections.emptyList();
}
return result;
}
@SuppressWarnings("deprecation")
public void addHelperOperation(C owner, O operation) {
addOperation(owner, operation);
}
/**
* Allows subclasses to add a newly OCL-defined additional operation to
* the environment. This should be called by the subclass's implementation
* of the {@link Environment#defineOperation} method.
*
* @param owner the classifier in which context the attribute is defined
* @param operation the additional operation
*
* @deprecated Since 1.2, use the
* {@link Environment.Internal#addHelperOperation(Object, Object)}
* API, instead
*/
@Deprecated
protected void addOperation(C owner, O operation) {
if (getInternalParent() != null) {
// propagate additional operations as high as possible so that they
// will be accessible to all child environments of the root
getInternalParent().addHelperOperation(owner, operation);
} else {
getTypeResolver().resolveAdditionalOperation(owner, operation);
}
}
public List getAdditionalOperations(C classifier) {
if (getInternalParent() != null) {
return getInternalParent().getAdditionalOperations(classifier);
}
List result = null;
TypeResolver res = getTypeResolver();
List additionals = res.getAdditionalOperations(classifier);
if (!additionals.isEmpty()) {
result = new java.util.ArrayList(additionals);
}
Collection extends C> allParents = (classifier instanceof PredefinedType>)
? OCLStandardLibraryUtil.getAllSupertypes(this,
(PredefinedType>) classifier)
: getUMLReflection().getAllSupertypes(classifier);
for (C general : allParents) {
additionals = res.getAdditionalOperations(general);
if (!additionals.isEmpty()) {
if (result == null) {
result = new java.util.ArrayList(additionals);
} else {
result.addAll(additionals);
}
}
}
if (result == null) {
result = Collections.emptyList();
}
return result;
}
// implements the interface method
public void setInitConstraint(P property, CT constraint) {
if (getInternalParent() != null) {
// propagate initializers as high as possible so that they
// will be accessible to all child environments of the root
getInternalParent().setInitConstraint(property, constraint);
} else {
propertyInitializers.put(property, constraint);
}
}
// implements the interface method
public CT getInitConstraint(P property) {
if (getInternalParent() != null) {
return getInternalParent().getInitConstraint(property);
}
return propertyInitializers.get(property);
}
// implements the interface method
public void setDeriveConstraint(P property, CT constraint) {
if (getInternalParent() != null) {
// propagate derivations as high as possible so that they
// will be accessible to all child environments of the root
getInternalParent().setDeriveConstraint(property, constraint);
} else {
propertyDerivations.put(property, constraint);
}
}
// implements the interface method
public CT getDeriveConstraint(P property) {
if (getInternalParent() != null) {
return getInternalParent().getDeriveConstraint(property);
}
return propertyDerivations.get(property);
}
// implements the interface method
public void setBodyCondition(O operation, CT constraint) {
if (getInternalParent() != null) {
// propagate bodies as high as possible so that they
// will be accessible to all child environments of the root
getInternalParent().setBodyCondition(operation, constraint);
} else {
operationBodies.put(operation, constraint);
}
}
// implements the interface method
public CT getBodyCondition(O operation) {
if (getInternalParent() != null) {
return getInternalParent().getBodyCondition(operation);
}
return operationBodies.get(operation);
}
// implements the interface method
public Variable lookupLocal(String name) {
// support operation parameters whose names need to be escaped in OCL
Variable result = doLookupLocal(name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupLocal(AbstractOCLAnalyzer.unescape(name));
}
return result;
}
private Variable doLookupLocal(String name) {
for (int i = 0; i < namedElements.size(); i++) {
VariableEntry elem = namedElements.get(i);
if (elem.name.equals(name)) {
return elem.variable;
}
}
return null;
}
// implements the interface method
public Variable lookup(String name) {
Variable elem = lookupLocal(name);
if (elem != null) {
return elem;
}
if (getInternalParent() != null) {
return getInternalParent().lookup(name);
} else {
return null;
}
}
// implements the interface method
public O lookupOperation(C owner, String name, List extends TypedElement> args) {
O result = doLookupOperation(owner, name, args);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupOperation(owner, AbstractOCLAnalyzer.unescape(name), args);
}
return result;
}
private O doLookupOperation(C owner, String name, List extends TypedElement> args) {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForOperation(name, args);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
return TypeUtil.findOperationMatching(this, owner, name, args);
}
// implements the interface method
public P lookupProperty(C owner, String name) {
P result = doLookupProperty(owner, name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupProperty(owner, AbstractOCLAnalyzer.unescape(name));
}
return result;
}
private P doLookupProperty(C owner, String name) {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForProperty(name);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
return TypeUtil.findAttribute(this, owner, name);
}
// implements the interface method
public C lookupAssociationClassReference(C owner, String name) {
C result = doLookupAssociationClassReference(owner, name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupAssociationClassReference(owner, AbstractOCLAnalyzer
.unescape(name));
}
return result;
}
// implements the interface method
private C doLookupAssociationClassReference(C owner, String name) {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForAssociationClass(name);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
C result = null;
List properties = getUMLReflection().getAttributes(owner);
Iterator
iter = properties.iterator();
while ((result == null) && iter.hasNext()) {
P next = iter.next();
C assocClass = getUMLReflection().getAssociationClass(next);
if ((assocClass != null) && name.equals(initialLower(assocClass))) {
result = assocClass;
}
}
return result;
}
// implements the interface method
public C lookupSignal(C owner, String name, List extends TypedElement> args) {
C result = doLookupSignal(owner, name, args);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupSignal(owner, AbstractOCLAnalyzer.unescape(name), args);
}
return result;
}
private C doLookupSignal(C owner, String name, List extends TypedElement> args) {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForSignal(name, args);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
return TypeUtil.findSignalMatching(this, owner,
getUMLReflection().getSignals(owner), name, args);
}
// implements the interface method
public S lookupState(C owner, List path) throws LookupException {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForState(path);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
int lastIndex = path.size() - 1;
String lastName = path.get(lastIndex);
List states = getStates(owner, path.subList(0, lastIndex));
S result = null;
for (S next : states) {
String nextName = getUMLReflection().getName(next);
boolean matched = lastName.equals(nextName);
if (!matched && AbstractOCLAnalyzer.isEscaped(lastName)) {
matched = AbstractOCLAnalyzer.unescape(lastName).equals(nextName);
}
if (matched) {
if (result == null) {
result = next;
} else {
// ambiguous reference to the state
String msg = OCLMessages.bind(
OCLMessages.AmbiguousState_ERROR_,
path,
getUMLReflection().getQualifiedName(owner));
throw new LookupException(msg);
}
}
}
return result;
}
// implements the interface method
public Variable lookupImplicitSourceForOperation(
String name,
List extends TypedElement> args) {
Variable vdcl;
for (int i = namedElements.size() - 1; i >= 0; i--) {
VariableEntry element = namedElements.get(i);
vdcl = element.variable;
C owner = vdcl.getType();
if (!element.isExplicit && (owner != null)) {
O eop = lookupOperation(owner, name, args);
if (eop != null) {
return vdcl;
}
}
}
// try the "self" variable, last
vdcl = getSelfVariable();
if (vdcl != null) {
C owner = vdcl.getType();
if (owner != null) {
O eop = lookupOperation(owner, name, args);
if (eop != null) {
return vdcl;
}
}
}
return null;
}
// implements the interface method
public Variable lookupImplicitSourceForProperty(String name) {
Variable vdcl;
for (int i = namedElements.size() - 1; i >= 0; i--) {
VariableEntry element = namedElements.get(i);
vdcl = element.variable;
C owner = vdcl.getType();
if (!element.isExplicit && (owner != null)) {
P property = safeTryLookupProperty(owner, name);
if (property != null) {
return vdcl;
}
}
}
// try the "self" variable, last
vdcl = getSelfVariable();
if (vdcl != null) {
C owner = vdcl.getType();
if (owner != null) {
P property = safeTryLookupProperty(owner, name);
if (property != null) {
return vdcl;
}
}
}
return null;
}
/**
* Wrapper for the "try" operation that doesn't throw, but just returns the
* first ambiguous match in case of ambiguity.
*/
@SuppressWarnings("unchecked")
private P safeTryLookupProperty(C owner, String name) {
P result = null;
try {
result = tryLookupProperty(owner, name);
} catch (LookupException e) {
if (!e.getAmbiguousMatches().isEmpty()) {
result = (P) e.getAmbiguousMatches().get(0);
}
}
return result;
}
// implements the interface method
public Variable lookupImplicitSourceForAssociationClass(String name) {
Variable vdcl;
for (int i = namedElements.size() - 1; i >= 0; i--) {
VariableEntry element = namedElements.get(i);
vdcl = element.variable;
C owner = vdcl.getType();
if (!element.isExplicit && (owner != null)) {
C ac = lookupAssociationClassReference(owner, name);
if (ac != null) {
return vdcl;
}
}
}
// try the "self" variable, last
vdcl = getSelfVariable();
if (vdcl != null) {
C owner = vdcl.getType();
if (owner != null) {
C ac = lookupAssociationClassReference(owner, name);
if (ac != null) {
return vdcl;
}
}
}
return null;
}
// implements the interface method
public Variable lookupImplicitSourceForSignal(
String name,
List extends TypedElement> args) {
Variable vdcl;
for (int i = namedElements.size() - 1; i >= 0; i--) {
VariableEntry element = namedElements.get(i);
vdcl = element.variable;
C owner = vdcl.getType();
if (!element.isExplicit && (owner != null)) {
C sig = lookupSignal(owner, name, args);
if (sig != null) {
return vdcl;
}
}
}
// try the "self" variable, last
vdcl = getSelfVariable();
if (vdcl != null) {
C owner = vdcl.getType();
if (owner != null) {
C sig = lookupSignal(owner, name, args);
if (sig != null) {
return vdcl;
}
}
}
return null;
}
// implements the interface method
public Variable lookupImplicitSourceForState(List path)
throws LookupException {
Variable vdcl;
for (int i = namedElements.size() - 1; i >= 0; i--) {
VariableEntry element = namedElements.get(i);
vdcl = element.variable;
C owner = vdcl.getType();
if (!element.isExplicit && (owner != null)) {
S state = lookupState(owner, path);
if (state != null) {
return vdcl;
}
}
}
// try the "self" variable, last
vdcl = getSelfVariable();
if (vdcl != null) {
C owner = vdcl.getType();
if (owner != null) {
S state = lookupState(owner, path);
if (state != null) {
return vdcl;
}
}
}
return null;
}
/**
* Gets the name of a named element
with its initial character
* in lower case.
*
* @param element a named element
* @return the element's name, with an initial lower case letter
*/
protected String initialLower(Object element) {
String name = getUMLReflection().getName(element);
if (name == null) {
return null;
}
StringBuffer result = new StringBuffer(name);
if (result.length() > 0) {
UnicodeSupport.setCodePointAt(
result,
0,
UnicodeSupport.toLowerCase(UnicodeSupport.codePointAt(result, 0)));
}
return result.toString();
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupAssociationClassReference(Object, String)} method.
*
* @since 1.2
*/
public C tryLookupAssociationClassReference(C owner, String name)
throws LookupException {
return lookupAssociationClassReference(owner, name);
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupClassifier(List)} method.
*
* @since 1.2
*/
public C tryLookupClassifier(List names)
throws LookupException {
return lookupClassifier(names);
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupOperation(Object, String, List)} method.
*
* @since 1.2
*/
public O tryLookupOperation(C owner, String name,
List extends TypedElement> args)
throws LookupException {
return lookupOperation(owner, name, args);
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupSignal(Object, String, List)} method.
*
* @since 1.2
*/
public C tryLookupSignal(C owner, String name,
List extends TypedElement> args)
throws LookupException {
return lookupSignal(owner, name, args);
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupPackage(List)} method.
*
* @since 1.2
*/
public PK tryLookupPackage(List names)
throws LookupException {
return lookupPackage(names);
}
/**
* This default implementation simply delegates to the
* {@link Environment#lookupProperty(Object, String)} method.
*
* @since 1.2
*/
public P tryLookupProperty(C owner, String name)
throws LookupException {
P result = lookupProperty(owner, name);
if (result == null) {
// looks up non-navigable named ends as well as unnamed ends. Hence
// the possibility of ambiguity
result = lookupNonNavigableEnd(owner, name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = lookupNonNavigableEnd(owner, AbstractOCLAnalyzer.unescape(name));
}
}
return result;
}
/**
* Looks up a non-navigable association end on behalf of
* the specified owner
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 null
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
*/
protected P lookupNonNavigableEnd(C owner, String name) throws LookupException {
if (owner == null) {
Variable vdcl = lookupImplicitSourceForProperty(name);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
List matches = new java.util.ArrayList
(2);
findNonNavigableAssociationEnds(owner, name, matches);
if (matches.isEmpty()) {
// search for unnamed ends (named but non-navigable ends take priority)
findUnnamedAssociationEnds(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);
}
/**
* Searches for non-navigable association ends with the specified
* name at the given classifier'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
*/
protected void findNonNavigableAssociationEnds(C classifier, String name, List
ends) {
// no default implementation
}
/**
* Searches for unnamed association ends using the specified name
* at the given classifier's end of an association.
* Subclasses should reimplement this method if they support non-navigable
* association ends. It is expected, in OCL, that the supplied name
* is the {@linkplain #initialLower(Object) initial-lower-case name} of the
* type of the unnamed end.
*
* @param classifier a classifier at an association end
* @param name the initial-lower classifier name to look for
* @param ends collects the ends found by the subclass implementation
*/
protected void findUnnamedAssociationEnds(C classifier, String name, List
ends) {
// no default implementation
}
/**
* Queries whether I have a non-OK setting for the specified problem option.
* In such cases, I will need to be concerned with reporting the problem.
*
* @param option the problem option
* @return whether I have a setting for it that is not OK
*
* @see ProblemHandler.Severity#OK
*/
public boolean notOK(Option option) {
ProblemHandler.Severity sev = getValue(option);
return (sev != null) && !sev.isOK();
}
/**
* I dispose my type resolver, if it is an {@link AbstractTypeResolver}
* and I am the root environment (which owns the resolver).
*
* @since 1.2
*/
public void dispose() {
if ((getInternalParent() == null)
&& (getTypeResolver() instanceof AbstractTypeResolver, ?, ?, ?, ?>)) {
((AbstractTypeResolver, ?, ?, ?, ?>) getTypeResolver()).dispose();
}
}
/**
* Obtains my extensible type checker utility. If it has not already been
* initialized before, then it is lazily {@linkplain #createTypeChecker()}.
*
* @return my type-checker
*
* @since 1.3
*
* @see #createTypeChecker()
*/
protected TypeChecker getTypeChecker() {
if (typeChecker == null) {
typeChecker = createTypeChecker();
}
return typeChecker;
}
/**
* Creates my extensible type checker utility when it is first needed.
* A default implementation is supplied, which subclasses may replace by
* overriding this method.
*
* @return a new type-checker
*
* @since 1.3
*
* @see #getTypeChecker()
*/
protected TypeChecker createTypeChecker() {
return new AbstractTypeChecker(this) {
@Override
protected C resolve(C type) {
return getTypeResolver().resolve(type);
}
@Override
protected CollectionType resolveCollectionType(
CollectionKind kind, C elementType) {
return getTypeResolver().resolveCollectionType(kind, elementType);
}
@Override
protected TupleType resolveTupleType(
EList extends TypedElement> parts) {
return getTypeResolver().resolveTupleType(parts);
}
};
}
/**
* Since {@link AbstractTypeResolver} implements {@link TypeChecker},
* AbstractEnvironment will try to adapt {@link TypeChecker}, via its
* {@link TypeResolver}.
*
* @since 1.3
*
* @see org.eclipse.ocl.lpg.AbstractBasicEnvironment#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public T getAdapter(Class adapterType) {
if (adapterType == TypeChecker.class) {
return (T) getTypeChecker();
}
return super.getAdapter(adapterType);
}
//
// Nested classes
//
/**
* Wrapper for OCL variable declarations that additionally tracks whether
* they are explicit or implicit variables.
*
* @author Christian W. Damus (cdamus)
*/
protected final class VariableEntry {
final String name;
final Variable variable;
final boolean isExplicit;
VariableEntry(String name, Variable variable, boolean isExplicit) {
this.name = name;
this.variable = variable;
this.isExplicit = isExplicit;
}
/**
* @since 3.1
*/
public Variable getVariable() {
return variable;
}
/**
* @since 3.1
*/
public boolean isExplicit() {
return isExplicit;
}
@Override
public String toString() {
return "VariableEntry[" + name + ", " //$NON-NLS-1$//$NON-NLS-2$
+ (isExplicit? "explicit, " : "implicit, ") + variable + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
}