| /** |
| * <copyright> |
| * |
| * Copyright (c) 2007,2011 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 |
| * E.D.Willink - Refactoring to support extensibility and flexible error handling |
| * Zeligsoft - Bug 251349 |
| * E.D.Willink - Bug 259818 |
| * Radek Dvorak - Bug 261128 |
| * |
| * </copyright> |
| * |
| * $Id: OCLBase.java,v 1.7 2011/04/20 19:02:47 ewillink Exp $ |
| */ |
| package org.eclipse.ocl.examples.pivot; |
| |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.ocl.examples.domain.elements.DomainStandardLibrary; |
| import org.eclipse.ocl.examples.domain.evaluation.EvaluationHaltedException; |
| import org.eclipse.ocl.examples.domain.evaluation.InvalidEvaluationException; |
| import org.eclipse.ocl.examples.domain.evaluation.DomainModelManager; |
| import org.eclipse.ocl.examples.domain.values.Value; |
| import org.eclipse.ocl.examples.domain.values.ValueFactory; |
| import org.eclipse.ocl.examples.pivot.evaluation.EvaluationEnvironment; |
| import org.eclipse.ocl.examples.pivot.evaluation.EvaluationVisitor; |
| import org.eclipse.ocl.examples.pivot.manager.MetaModelManager; |
| import org.eclipse.ocl.examples.pivot.util.PivotPlugin; |
| import org.eclipse.ocl.examples.pivot.utilities.QueryBaseImpl; |
| |
| /** |
| * <p> |
| * The Façade for parsing and evaluation of OCL documents and |
| * constraints. The <code>OCL</code> is initialized with an environment factory |
| * that supports the metamodel of the model to which the OCL constraints are to |
| * be applied. |
| * </p> |
| * <p> |
| * Since 1.2, the helper supplies {@linkplain #getProblems() diagnostics} |
| * indicating any problems encountered while parsing. The diagnostics pertain |
| * always to the most recently executed parse operation. |
| * </p> |
| * <p> |
| * See the {@link Environment} class for a description of the generic type |
| * parameters of this class. |
| * </p> |
| * <p> |
| * Providers of OCL bindings for metamodels may extend this class for the |
| * convenience of their clients, to specify the type parameter substitutions. |
| * </p> |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| public abstract class OCLBase { |
| |
| private final EnvironmentFactory environmentFactory; |
| |
| private final Environment rootEnvironment; |
| |
| private EvaluationEnvironment evalEnv; |
| |
| private DomainModelManager modelManager; |
| |
| private List<Constraint> constraints = new java.util.ArrayList<Constraint>(); |
| |
| private Diagnostic problems; |
| private Diagnostic evaluationProblems; |
| |
| private int parserRepairCount = 0; |
| |
| private boolean traceParsing = PivotPlugin |
| .shouldTrace(OCLDebugOptions.PARSING); |
| |
| private boolean traceEvaluation = PivotPlugin |
| .shouldTrace(OCLDebugOptions.EVALUATION); |
| |
| /** |
| * Initializes me with an environment factory, which I will use to create a |
| * root environment. |
| * |
| * @param envFactory |
| * my environment factory |
| */ |
| protected OCLBase( |
| EnvironmentFactory envFactory) { |
| this(envFactory, envFactory.createEnvironment()); |
| } |
| |
| /** |
| * Initializes me with a root environment. |
| * |
| * @param env |
| * my root environment |
| */ |
| protected OCLBase(Environment env) { |
| this(env.getFactory(), env); |
| } |
| |
| /** |
| * Initializes me with an environment factory and a resource in which my |
| * environment is to be persisted, or from which it may be loaded if the |
| * resource already has content. |
| * |
| * @param envFactory |
| * an environment factory |
| * @param resource |
| * a resource in which the root environment is persisted |
| */ |
| protected OCLBase( |
| EnvironmentFactory envFactory, |
| Resource resource) { |
| this(envFactory, envFactory.loadEnvironment(resource)); |
| } |
| |
| /** |
| * Initializes me with my environment factory and root environment. |
| * |
| * @param envFactory |
| * my environment factory |
| * @param rootEnv |
| * my root environment |
| */ |
| protected OCLBase( |
| EnvironmentFactory envFactory, |
| Environment rootEnv) { |
| this.environmentFactory = envFactory; |
| this.rootEnvironment = rootEnv; |
| |
| if (envFactory instanceof AbstractEnvironmentFactory) { |
| AbstractEnvironmentFactory abstractFactory = (AbstractEnvironmentFactory) envFactory; |
| |
| abstractFactory.setEvaluationTracingEnabled(traceEvaluation); |
| } |
| } |
| |
| /** |
| * Creates a new <code>OCL</code> with an environment factory, which it will |
| * use to create a root environment. |
| * |
| * @param envFactory |
| * an environment factory |
| * @return the OCL façade |
| * |
| public static OCLBase newInstance( |
| EnvironmentFactory envFactory) { |
| |
| return new OCLBase(envFactory); |
| } */ |
| |
| /** |
| * Creates a new <code>OCL</code> on the specified root environment. |
| * |
| * @param env |
| * the root environment |
| * @return the OCL façade |
| * |
| public static OCLBase newInstance( |
| Environment env) { |
| |
| return new OCLBase(env); |
| } */ |
| |
| /** |
| * Creates a new <code>OCL</code> with an environment factory and a resource |
| * in which its environment is to be persisted, or from which it may be |
| * loaded if the resource already has content. |
| * |
| * @param envFactory |
| * an environment factory |
| * @param resource |
| * a resource in which the root environment is persisted |
| * @return the OCL façade |
| * |
| public static OCLBase newInstance( |
| EnvironmentFactory envFactory, |
| Resource resource) { |
| |
| return new OCLBase( |
| envFactory, resource); |
| } */ |
| |
| /** |
| * Obtains the OCL parsing environment. Clients may manipulate this |
| * environment to support custom requirements, such as adding variables to |
| * it to define "global" values. For any variables that are added, bindings |
| * will have to provided, as well, in the |
| * {@linkplain #getEvaluationEnvironment() evaluation environment}. |
| * |
| * @return the parsing environment |
| * |
| * @see #getEvaluationEnvironment() |
| */ |
| public Environment getEnvironment() { |
| return rootEnvironment; |
| } |
| |
| public EnvironmentFactory getEnvironmentFactory() { |
| return environmentFactory; |
| } |
| |
| /** |
| * Obtains the OCL evaluation environment. Clients may manipulate this |
| * environment to support custom requirements, such as binding the values of |
| * "global" variables. |
| * |
| * @return the evaluation environment |
| * |
| * @see #getEnvironment() |
| */ |
| public EvaluationEnvironment getEvaluationEnvironment() { |
| if (evalEnv == null) { |
| evalEnv = environmentFactory.createEvaluationEnvironment(); |
| } |
| |
| return evalEnv; |
| } |
| |
| /** |
| * Obtains the model manager, if any, provided by the client to customize the |
| * evaluation of constraints. |
| * |
| * @return the client-provided custom model manager, or <code>null</code> if |
| * thie OCL is using the default dynamic extent map implementation |
| */ |
| public DomainModelManager getModelManager() { |
| return modelManager; |
| } |
| |
| /** |
| * Assigns a custom extent map to define the extents of classes in |
| * evaluation of OCL constraints. This is only needed if the default dynamic |
| * extent-map implementation is not suitable. |
| * |
| * @param modelManager |
| * a custom extent map, or <code>null</code> to use the default |
| * dynamic extent map implementation |
| */ |
| public void setModelManager(DomainModelManager modelManager) { |
| this.modelManager = modelManager; |
| } |
| |
| /** |
| * Obtains all of the constraints parsed hitherto by this OCL instance. |
| * These accumulate with every document that is parsed. |
| * |
| * @return the constraints that I have parsed |
| * |
| * @see #parse(OCLInput) |
| */ |
| public List<Constraint> getConstraints() { |
| return constraints; |
| } |
| |
| /** |
| * Validates an OCL expression, which may have been loaded from some |
| * resource or constructed via the API (perhaps by translation from some |
| * other language). |
| * |
| * @param expression |
| * an expression to validate |
| * |
| * @throws SemanticException |
| * on detection of any well-formedness problem in the expression |
| * |
| * @see #validate(Object) |
| */ |
| public void validate(OclExpression expression) throws SemanticException { |
| throw new UnsupportedOperationException(getClass().getName() + ".validate"); |
| // clear out old diagnostics |
| /* ProblemHandler ph = OCLUtil.getAdapter(rootEnvironment, |
| ProblemHandler.class); |
| if (ph != null) { |
| ph.beginValidation(); |
| } |
| |
| expression.accept(ValidationVisitor.getInstance(rootEnvironment)); |
| |
| if (ph != null) { |
| ph.endValidation(); |
| |
| try { |
| OCLUtil.checkForErrors(ph); |
| } catch (SyntaxException e) { |
| // shouldn't actually be able to get this from validation |
| throw new SemanticException(e.getDiagnostic()); |
| } |
| } */ |
| } |
| |
| /** |
| * Validates an OCL constraint, which may have been loaded from some |
| * resource or constructed via the API (perhaps by translation from some |
| * other language). |
| * |
| * @param constraint |
| * a constraint to validate |
| * |
| * @throws SemanticException |
| * on detection of any well-formedness problem in the constraint |
| */ |
| public void validate(Constraint constraint) throws SemanticException { |
| throw new UnsupportedOperationException(getClass().getName() + ".validate"); |
| // clear out old diagnostics |
| /* ProblemHandler ph = OCLUtil.getAdapter(rootEnvironment, |
| ProblemHandler.class); |
| if (ph != null) { |
| ph.beginValidation(); |
| } |
| |
| ValidationVisitor.getInstance(rootEnvironment).visitConstraint( |
| constraint); |
| |
| if (ph != null) { |
| ph.endValidation(); |
| |
| try { |
| OCLUtil.checkForErrors(ph); |
| } catch (SyntaxException e) { |
| // shouldn't actually be able to get this from validation |
| throw new SemanticException(e.getDiagnostic()); |
| } |
| } */ |
| } |
| |
| /** |
| * Evaluates a query expression on a context object (which is bound to the |
| * <tt>self</tt> variable). Clients should use the |
| * {@link #isInvalid(Object)} method to check whether the evaluation result |
| * is <tt>OclInvalid</tt>. |
| * |
| * @param context |
| * the context (self) object |
| * @param expression |
| * the OCL expression to evaluate |
| * |
| * @return the value of the expression, or <tt>OclInvalid</tt> if the |
| * evaluation fails for reasons other than a run-time exception |
| * |
| * @see #isInvalid(Object) |
| * @see #check(Object, Object) |
| */ |
| public org.eclipse.ocl.examples.domain.values.Value evaluate(Object context, ExpressionInOcl expression) { |
| evaluationProblems = null; |
| |
| // can determine a more appropriate context from the context |
| // variable of the expression, to account for stereotype constraints |
| // context = HelperUtil.getConstraintContext(rootEnvironment, context, expression); |
| EvaluationEnvironment localEvalEnv = getEvaluationEnvironment(); |
| ValueFactory valueFactory = localEvalEnv.getValueFactory(); |
| Value value = valueFactory.valueOf(context); |
| localEvalEnv.add(expression.getContextVariable(), value); |
| // if ((value != null) && !value.isUndefined()) { |
| // expression.getContextVariable().setValue(value); |
| // } |
| DomainModelManager extents = getModelManager(); |
| if (extents == null) { |
| // let the evaluation environment create one |
| extents = localEvalEnv.createModelManager(context); |
| } |
| |
| EvaluationVisitor ev = environmentFactory |
| .createEvaluationVisitor(rootEnvironment, localEvalEnv, extents); |
| |
| org.eclipse.ocl.examples.domain.values.Value result; |
| |
| try { |
| result = expression.accept(ev); |
| } catch (EvaluationHaltedException e) { |
| evaluationProblems = e.getDiagnostic(); |
| throw e; |
| } finally { |
| localEvalEnv.remove(expression.getContextVariable()); |
| } |
| if (result == null) { |
| return localEvalEnv.throwInvalidEvaluation("Java-Null value"); |
| } |
| if (result.isInvalid()) { |
| return localEvalEnv.throwInvalidEvaluation("Invalid Value"); |
| } |
| return result; |
| } |
| |
| /** |
| * Queries whether a value is the special <tt>invalid</tt> token. This is |
| * useful for determining whether the result of an expression evaluation is |
| * valid. |
| * |
| * @param value |
| * some OCL value |
| * @return <code>true</code> if it is the <tt>invalid</tt>; |
| * <code>false</code>, otherwise |
| * |
| * @see #evaluate(Object, OCLExpression) |
| */ |
| // public boolean isInvalid(Object value) { |
| // return getEnvironment().getOCLStandardLibrary().getInvalidValue() == value; |
| // } |
| |
| /** |
| * Checks whether a constraint is satisfied by an object. If the constraint |
| * is an invariant constraint, then no additional variable bindings are |
| * required. If it is an operation precondition or postcondition, however, |
| * then the appropriate parameter variables and (in the postcondition case) |
| * result variable should be bound in the evaluation environment. |
| * |
| * @param context |
| * the <tt>self</tt> object of the constraint |
| * @param constraint |
| * the constraint to check |
| * |
| * @return whether the context object satisfies the constraint |
| * |
| * @see #check(Object, OCLExpression) |
| * @see #evaluate(Object, OCLExpression) |
| */ |
| public boolean check(Object context, Constraint constraint) { |
| ExpressionInOcl specification = (ExpressionInOcl) constraint.getSpecification(); |
| |
| return check(context, specification); |
| } |
| |
| /** |
| * Checks whether a constraint, specified simply as an OCL expression, is |
| * satisfied by an object. If the constraint is an invariant constraint, |
| * then no additional variable bindings are required. If it is an operation |
| * precondition or postcondition, however, then the appropriate parameter |
| * variables and (in the postcondition case) result variable should be bound |
| * in the evaluation environment. |
| * |
| * @param context |
| * the <tt>self</tt> object of the constraint |
| * @param constraint |
| * the constraint to check, which must be a boolean-valued |
| * expression |
| * |
| * @return whether the context object satisfies the constraint |
| * |
| * @see #check(Object, Object) |
| * @see #evaluate(Object, OCLExpression) |
| * |
| * @throws IllegalArgumentException |
| * if the constraint expression is not boolean-valued |
| */ |
| public boolean check(Object context, ExpressionInOcl specification) { |
| DomainStandardLibrary stdlib = getEnvironment().getOCLStandardLibrary(); |
| if (specification.getBodyExpression().getType() != stdlib.getBooleanType()) { |
| throw new IllegalArgumentException("constraint is not boolean"); //$NON-NLS-1$ |
| } |
| try { |
| Value result = evaluate(context, specification); |
| return result.isTrue(); |
| } catch (InvalidEvaluationException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Creates a new {@link OCLHelper} instance for convenient parsing of |
| * embedded constraints and query expressions in this environment. The |
| * helper is particulary useful for parsing constraints embedded in the |
| * model, in which case the context of a constraint is determined by its |
| * placement and the textual context declarations are unnecessary. |
| * |
| * @return a new helper object |
| */ |
| // public OCLHelper createOCLHelper() { |
| // return HelperUtil.createOCLHelper(this); |
| // } |
| |
| /** |
| * Creates a new {@link QueryBase} encapsulating a query expression with the |
| * current environment and extent map. This is convenient for repeated |
| * evaluation of expressions and for filtering/transforming objects using a |
| * query or constraint expression. |
| * <p> |
| * Every query maintains its own evaluation environment, which enables |
| * concurrent evaluation (where this may be safe in an EMF-based model) and |
| * different bindings for client-supplied "global" variables. |
| * </p> |
| * |
| * @param query |
| * the OCL query expression, which may be interpreted as a |
| * constraint if it is boolean-valued |
| * |
| * @return the new query object |
| * |
| * @see #createQuery(Object) |
| */ |
| public QueryBase createQuery(ExpressionInOcl specification) { |
| return new QueryBaseImpl(rootEnvironment, specification, modelManager); |
| } |
| |
| /** |
| * Creates a new {@link QueryBase} encapsulating a constraint with the current |
| * environment and extent map. This is convenient for repeated evaluation of |
| * constraints and for filtering objects using the constraint expression. |
| * <p> |
| * Every query maintains its own evaluation environment, which enables |
| * concurrent evaluation (where this may be safe in an EMF-based model) and |
| * different bindings for client-supplied "global" variables. |
| * </p> |
| * |
| * @param constraint |
| * the OCL constraint |
| * |
| * @return the new query object |
| * |
| * @see #createQuery(OCLExpression) |
| */ |
| public QueryBase createQuery(Constraint constraint) { |
| return new QueryBaseImpl( |
| rootEnvironment, (ExpressionInOcl) constraint.getSpecification(), modelManager); |
| } |
| |
| /** |
| * Queries whether tracing of parsingis 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/parsing</tt> debug option. |
| * </p> |
| * |
| * @return whether parse tracing is enabled |
| * |
| * @see #setParseTracingEnabled(boolean) |
| */ |
| public boolean isParseTracingEnabled() { |
| return traceParsing; |
| } |
| |
| /** |
| * Sets whether tracing of parsing 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/parsing</tt> debug option. |
| * </p> |
| * |
| * param b whether parsing tracing is enabled |
| * |
| * @see #isParseTracingEnabled() |
| */ |
| public void setParseTracingEnabled(boolean b) { |
| traceParsing = b; |
| } |
| |
| /** |
| * <p> |
| * Queries the number of repairs to be made by the parser. |
| * </p> |
| * <p> |
| * The default zero value selects use of the deterministic parser, which |
| * terminates after one serious syntax error is detected. |
| * </p> |
| * <p> |
| * A non-zero value selects the backtracking parser. The backtracking parser |
| * may be about three times slower. |
| * </p> |
| * |
| * @return the number of repairs to be attempted |
| * |
| * @see #setParserRepairCount(int) |
| */ |
| public int getParserRepairCount() { |
| return parserRepairCount; |
| } |
| |
| /** |
| * <p> |
| * Sets the number of repairs to be made by the parser. |
| * </p> |
| * <p> |
| * The default zero value selects use of the deterministic parser, which |
| * terminates after one serious syntax error is detected. |
| * </p> |
| * <p> |
| * A non-zero value selects the backtracking parser. The backtracking parser |
| * may be about three times slower. |
| * </p> |
| * |
| * @param parserRepairCount |
| * whether evaluation tracing is enabled |
| * |
| * @throws IllegalArgumentException |
| * if the <tt>parserRepairCount</tt> is negative |
| * |
| * @see #getParserRepairCount() |
| */ |
| public void setParserRepairCount(int parserRepairCount) { |
| if (parserRepairCount < 0) { |
| throw new IllegalArgumentException("negative repair count"); //$NON-NLS-1$ |
| } |
| |
| this.parserRepairCount = parserRepairCount; |
| } |
| |
| /** |
| * 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) |
| */ |
| public 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() |
| */ |
| public void setEvaluationTracingEnabled(boolean b) { |
| traceEvaluation = b; |
| |
| if (environmentFactory instanceof AbstractEnvironmentFactory) { |
| AbstractEnvironmentFactory abstractFactory = (AbstractEnvironmentFactory) environmentFactory; |
| |
| abstractFactory.setEvaluationTracingEnabled(traceEvaluation); |
| } |
| } |
| |
| /** |
| * Obtains problems, if any, found in parsing the last OCL constraint or |
| * query expression. |
| * |
| * @return parsing problems or <code>null</code> if all was OK |
| */ |
| public Diagnostic getProblems() { |
| return problems; |
| } |
| |
| public MetaModelManager getMetaModelManager() { |
| return rootEnvironment.getMetaModelManager(); |
| } |
| |
| public ValueFactory getValueFactory() { |
| return getMetaModelManager().getValueFactory(); |
| } |
| |
| /** |
| * Obtains problems, if any, occurred during evaluation of the last OCL |
| * constraint or query expression. |
| * |
| * @return evaluation problems or <code>null</code> if all was OK |
| */ |
| public Diagnostic getEvaluationProblems() { |
| return evaluationProblems; |
| } |
| |
| /** |
| * Disposes any objects that I have created while I have been in use. This |
| * includes disposing of any {@link #getConstraints() constraints} that I |
| * have parsed and {@linkplain Environment.Internal#dispose() disposing} of |
| * my environment. |
| */ |
| public void dispose() { |
| // dispose of constraints by clearing their adapters |
| for (Constraint constraint : getConstraints()) { |
| EObject eObject = (EObject) constraint; |
| |
| if (eObject.eResource() == null) { |
| ObjectUtil.dispose(constraint); |
| } |
| } |
| |
| // forget the constraints |
| getConstraints().clear(); |
| |
| // dispose of my environment |
| if (getEnvironment() instanceof Environment.Internal) { |
| Environment.Internal env = (Environment.Internal) getEnvironment(); |
| env.dispose(); |
| } |
| getMetaModelManager().dispose(); |
| } |
| } |