blob: e177b2ec233869751c47f2610a44758d7739a5b0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2018 IBM Corporation, Zeligsoft Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - Initial API and implementation
* 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
*******************************************************************************/
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.ecore.resource.Resource;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.internal.evaluation.BasicTypeChecker;
import org.eclipse.ocl.internal.evaluation.CachedTypeChecker;
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.ParsingOptions;
import org.eclipse.ocl.options.ProblemOption;
import org.eclipse.ocl.parser.AbstractOCLAnalyzer;
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
* <code>Environment</code> interface from scratch.
* <p>
* In particular, this class provides:
* </p>
* <ul>
* <li>maintenance of the context package, classifier, operation, and property</li>
* <li>tracking of variable declarations</li>
* <li>lookup of operations, attributes, etc. by delegation to the other
* model introspection methods</li>
* <li>determination of implicit source variables for operation calls,
* attribute navigations, etc.</li>
* <li>tracking of operation bodies and attribute initializers/derivations</li>
* <li>storage of additional attributes and operations</li>
* </ul>
* <p>
* along with some subclass hook methods and convenience methods.
* </p>
* <p>
* See the {@link Environment} class for a description of the
* generic type parameters of this class.
* </p>
*
* @author Christian W. Damus (cdamus)
*/
public abstract class AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
extends AbstractBasicEnvironment
implements Environment.Internal<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>,
Environment.Lookup<PK, C, O, P> {
/* 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<VariableEntry> namedElements = new java.util.ArrayList<VariableEntry>();
private Variable<C, PM> selfVariable;
// map of operations to body conditions
private Map<O, CT> operationBodies = new java.util.HashMap<O, CT>();
// map of attributes to initial-value expressions
private Map<P, CT> propertyInitializers = new java.util.HashMap<P, CT>();
// map of attributes to derivation expressions
private Map<P, CT> propertyDerivations = new java.util.HashMap<P, CT>();
private TypeChecker<C, O, P> typeChecker;
/**
* Initializes me without a parent environment.
*/
protected AbstractEnvironment() {
super(null);
}
/**
* Initializes me with the specified parent environment.
*
* @param parent an environment (or <code>null</code>)
*/
protected AbstractEnvironment(
Environment.Internal<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <code>null</code>)
*/
protected AbstractEnvironment(
AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> getInternalParent() {
return (Environment.Internal<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) super.getParent();
}
// implements the interface method
/**
* @deprecated Since 1.2, use the {@link #getInternalParent()} method, instead.
*/
@SuppressWarnings("unchecked")
@Override
@Deprecated
public AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> getParent() {
return (AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) super.getParent();
}
/**
* Assigns me a parent environment after construction. It is not advisable
* to set the parent to <code>null</code> if I previously had one.
*
* @param parent my new parent
*/
public void setInternalParent(Environment.Internal<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent) {
super.setParent(parent);
}
/**
* Assigns me a parent environment after construction. It is not advisable
* to set the parent to <code>null</code> if I previously had one.
*
* @param parent my new parent
*/
protected void setParent(AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <em>not</em> 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<Variable<C, PM>> getVariables() {
Collection<Variable<C, PM>> result = new java.util.ArrayList<Variable<C, PM>>();
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<C, PM> parentVar : getInternalParent().getVariables()) {
if (lookupLocal(parentVar.getName()) == null) {
result.add(parentVar);
}
}
}
return result;
}
// implements the interface method
public boolean addElement(String name, Variable<C, PM> 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<C, PM> variable, boolean isExplicit) {
getResource().getContents().add(variable);
}
// implements the interface method
public void deleteElement(String name) {
for (Iterator<VariableEntry> 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<C, PM> variable, boolean isExplicit) {
getResource().getContents().remove(variable);
}
// implements the interface method
public void setSelfVariable(Variable<C, PM> 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<C, PM> getSelfVariable() {
Variable<C, PM> 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);
}
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<P> getAdditionalAttributes(C classifier) {
if (getInternalParent() != null) {
return getInternalParent().getAdditionalAttributes(classifier);
}
List<P> result = null;
TypeResolver<C, O, P> res = getTypeResolver();
List<P> additionals = res.getAdditionalAttributes(classifier);
if (!additionals.isEmpty()) {
result = new java.util.ArrayList<P>(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<P>(additionals);
} else {
result.addAll(additionals);
}
}
}
if (result == null) {
result = Collections.emptyList();
}
return result;
}
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<O> getAdditionalOperations(C classifier) {
if (getInternalParent() != null) {
return getInternalParent().getAdditionalOperations(classifier);
}
List<O> result = null;
TypeResolver<C, O, P> res = getTypeResolver();
List<O> additionals = res.getAdditionalOperations(classifier);
if (!additionals.isEmpty()) {
result = new java.util.ArrayList<O>(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<O>(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<C, PM> lookupLocal(String name) {
// support operation parameters whose names need to be escaped in OCL
Variable<C, PM> result = doLookupLocal(name);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupLocal(AbstractOCLAnalyzer.unescape(name));
}
return result;
}
private Variable<C, PM> 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 <C, PM>lookup(String name) {
Variable<C, PM> 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<C>> 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<C>> args) {
if (owner == null) {
Variable<C, PM> 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<C, PM> 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<C, PM> vdcl = lookupImplicitSourceForAssociationClass(name);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
C result = null;
List<P> properties = getUMLReflection().getAttributes(owner);
Iterator<P> 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<C>> 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<C>> args) {
if (owner == null) {
Variable<C, PM> 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<String> path) throws LookupException {
if (owner == null) {
Variable<C, PM> vdcl = lookupImplicitSourceForState(path);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
int lastIndex = path.size() - 1;
String lastName = path.get(lastIndex);
List<S> 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<C, PM> lookupImplicitSourceForOperation(
String name,
List<? extends TypedElement<C>> args) {
Variable<C, PM> 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<C, PM> lookupImplicitSourceForProperty(String name) {
Variable<C, PM> 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<C, PM> lookupImplicitSourceForAssociationClass(String name) {
Variable<C, PM> 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<C, PM> lookupImplicitSourceForSignal(
String name,
List<? extends TypedElement<C>> args) {
Variable<C, PM> 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<C, PM> lookupImplicitSourceForState(List<String> path)
throws LookupException {
Variable<C, PM> 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 <code>element</code> 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<String> 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<C>> 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<C>> 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<String> 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 <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
*/
protected P lookupNonNavigableEnd(C owner, String name) throws LookupException {
if (owner == null) {
Variable<C, PM> vdcl = lookupImplicitSourceForProperty(name);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
List<P> matches = new java.util.ArrayList<P>(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
* <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
*/
protected void findNonNavigableAssociationEnds(C classifier, String name, List<P> ends) {
// no default implementation
}
/**
* Searches for unnamed association ends using 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. It is expected, in OCL, that the supplied <tt>name</tt>
* 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<P> 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 org.eclipse.ocl.lpg.ProblemHandler.Severity#OK
*/
public boolean notOK(Option<ProblemHandler.Severity> 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<C, O, P> 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<C, O, P> createTypeChecker() {
boolean useTypeCaches = ParsingOptions.getValue(this, ParsingOptions.USE_TYPE_CACHES);
if (useTypeCaches) {
return new CachedTypeChecker<C, O, P, PM>(this);
}
else {
return new BasicTypeChecker<C, O, P, PM>(this);
}
}
/**
* 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> T getAdapter(Class<T> adapterType) {
if (adapterType == TypeChecker.class) {
return (T) getTypeChecker();
}
return super.getAdapter(adapterType);
}
/**
* @since 3.2
*/
protected void resetTypeCaches() {
if (typeChecker instanceof TypeChecker.Cached<?,?,?>) {
((TypeChecker.Cached<?,?,?>)typeChecker).reset();
}
}
//
// 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<C, PM> variable;
final boolean isExplicit;
VariableEntry(String name, Variable<C, PM> variable, boolean isExplicit) {
this.name = name;
this.variable = variable;
this.isExplicit = isExplicit;
}
/**
* @since 3.1
*/
public Variable<C, PM> 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$
}
}
}