blob: 7965a6ffaef2c2d0286d14c03103c1c9efa1f016 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2018 IBM Corporation, Borland Software Corp., 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
* Borland - Bug 265066
* Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Bug 333032
*******************************************************************************/
package org.eclipse.ocl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.helper.OCLSyntaxHelper;
import org.eclipse.ocl.internal.evaluation.TracingEvaluationVisitor;
import org.eclipse.ocl.parser.AbstractOCLParser;
import org.eclipse.ocl.parser.OCLAnalyzer;
import org.eclipse.ocl.parser.OCLFactoryWithHistory;
import org.eclipse.ocl.parser.ValidationVisitor;
import org.eclipse.ocl.util.Adaptable;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.ocl.utilities.Visitor;
/**
* Partial implementation of the {@link EnvironmentFactory} interface, useful
* for subclassing to define the OCL binding for a metamodel. This abstract
* class takes care of some of the more common (and mundane) chores, such as:
* <ul>
* <li>defining the "self" variable in the classifier context</li>
* <li>creating variables for operation parameters in an operation context</li>
* </ul>
* <p>
* The subclass's responsibility (in addition to implementing any other
* interface methods not implemented here) is to define how to:
* </p>
* <ul>
* <li>{@linkplain #getClassifier obtain} the classifier in the
* user model of an input element (which may already be a classifier
* or an may be an M0 instance)</li>
* <li>{@linkplain #lookupPackage lookup} a package in the model associated
* with this environment factory</li>
* </ul>
* <p>
* This class is intended to be extended by clients, for the purpose of
* implementing metamodel-specific environment factories.
* </p>
* <p>
* See the {@link EnvironmentFactory} class for a description of the
* generic type parameters of this class.
* </p>
*
* @author Christian W. Damus (cdamus)
*/
public abstract class AbstractEnvironmentFactory<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
implements EnvironmentFactory<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>,
Adaptable {
private boolean traceEvaluation;
/**
* Initializes me.
*/
protected AbstractEnvironmentFactory() {
super();
}
/**
* Creates an environment for the specified package context.
*
* @param parent the parent environment of the environment to be created
* @param context the package context (must not be <code>null</code>)
* @return the new nested environment
*/
protected Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createPackageContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
PK context) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result =
createEnvironment(parent);
if (result instanceof AbstractEnvironment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>) {
((AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) result)
.setContextPackage(context);
}
return result;
}
// implements the interface method
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createPackageContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
List<String> pathname) {
PK contextPackage = lookupPackage(pathname);
return (contextPackage == null)? null : createPackageContext(parent, contextPackage);
}
/**
* Looks up the package identified by the specified qualified name by
* whatever means is appropriate to the particular environment implementation.
*
* @param pathname the qualified name of the package to find
* @return the matching package, or <code>null</code> if none is found
*/
protected abstract PK lookupPackage(List<String> pathname);
// implements the interface method
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createClassifierContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
C context) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result =
createEnvironment(parent);
// in case it corresponds to an OCL primitive type
UMLReflection<PK, C, O, P, EL, PM, S, COA, SSA, CT> uml = parent.getUMLReflection();
context = uml.asOCLType(context);
Variable<C, PM> self = parent.getOCLFactory().createVariable();
uml.setName(self, Environment.SELF_VARIABLE_NAME);
uml.setType(self, context);
result.addElement(self.getName(), self, true);
result.setSelfVariable(self);
return result;
}
// implements the interface method
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createInstanceContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
Object context) {
return createClassifierContext(parent, getClassifier(context));
}
// implements the interface method
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createOperationContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
O operation) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result =
createEnvironment(parent);
if (result instanceof AbstractEnvironment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>) {
((AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) result)
.setContextOperation(operation);
}
UMLReflection<PK, C, O, P, EL, PM, S, COA, SSA, CT> uml = parent.getUMLReflection();
OCLFactory oclFactory = parent.getOCLFactory();
for (PM next : parent.getUMLReflection().getParameters(operation)) {
// ensure that we use the OCL primitive types wherever possible
Variable<C, PM> var = oclFactory.createVariable();
uml.setName(var, uml.getName(next));
uml.setType(var, TypeUtil.resolveType(result, uml.getOCLType(next)));
var.setRepresentedParameter(next);
result.addElement(var.getName(), var, true);
}
return result;
}
// implements the interface method
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
createAttributeContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
P property) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result =
createEnvironment(parent);
if (result instanceof AbstractEnvironment<?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?>) {
((AbstractEnvironment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>) result)
.setContextProperty(property);
}
return result;
}
// implements the interface method
public EvaluationVisitor<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createEvaluationVisitor(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
EvaluationEnvironment<C, O, P, CLS, E> evalEnv,
Map<? extends CLS, ? extends Set<? extends E>> extentMap) {
EvaluationVisitor<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result =
new EvaluationVisitorImpl<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(
env, evalEnv, extentMap);
if (isEvaluationTracingEnabled()) {
// decorate the evaluation visitor with tracing support
result = new TracingEvaluationVisitor<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(
result);
}
return result;
}
/**
* @since 3.1
*/
public OCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createOCLAnalyzer(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env, String input) {
return new OCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(env, input);
}
/**
* @since 3.1
*/
public OCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createOCLAnalyzer(
AbstractOCLParser parser) {
return new OCLAnalyzer<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(parser);
}
/**
* @since 3.1
*/
public OCLFactoryWithHistory createOCLFactoryWithHistory(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return new OCLFactoryWithHistory(env.getOCLFactory());
}
/**
* @since 3.1
*/
public OCLSyntaxHelper createOCLSyntaxHelper(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return new org.eclipse.ocl.internal.helper.OCLSyntaxHelper<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(env);
}
/**
* @since 3.1
*/
public Visitor<Boolean, C, O, P, EL, PM, S, COA, SSA, CT> createValidationVisitor(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return new ValidationVisitor<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>(env);
}
/**
* Obtains client metamodel's classifier for the specified
* <code>context</code> object, which may be an instance of a classifier
* in the user model or may actually be a classifier in the user model.
*
* @param context a context object or classifier
* @return the user model's classifier for this context object, or the
* context itself if it is a classifier
*/
protected abstract C getClassifier(Object context);
/**
* Queries whether tracing of evaluation is enabled. Tracing
* logs the progress of evaluation to the console, which may
* be of use in diagnosing problems.
* <p>
* In an Eclipse environment, tracing is also enabled by turning on the
* <tt>org.eclipse.ocl/debug/evaluation</tt> debug option.
* </p>
*
* @return whether evaluation tracing is enabled
*
* @see #setEvaluationTracingEnabled(boolean)
*/
protected boolean isEvaluationTracingEnabled() {
return traceEvaluation;
}
/**
* Sets whether tracing of evaluation is enabled. Tracing logs
* the progress of parsing to the console, which may be of use in diagnosing
* problems.
* <p>
* In an Eclipse environment, tracing is also enabled by turning on the
* <tt>org.eclipse.ocl/debug/evaluation</tt> debug option.
* </p>
*
* param b whether evaluation tracing is enabled
*
* @see #isEvaluationTracingEnabled()
*/
protected void setEvaluationTracingEnabled(boolean b) {
traceEvaluation = b;
}
/**
* The abstract environment factory implementation is adaptable. The
* default implementation adapts to and interface actually implemented by
* the receiver.
* <p>
* Subclasses may override or extend this implementation.
* </p>
*
* @since 1.2
*/
@SuppressWarnings("unchecked")
public <T> T getAdapter(Class<T> adapterType) {
T result;
if (adapterType.isAssignableFrom(getClass())) {
result = (T) this;
} else {
result = null;
}
return result;
}
}