All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ocl.OCL Maven / Gradle / Ivy

/**
 * 
 *
 * Copyright (c) 2007, 2010 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 - Bug 251349
 *   E.D.Willink - Bug 259818
 *   Radek Dvorak - Bug 261128
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Bug 333032
 *
 * 
 *
 * $Id: OCL.java,v 1.16 2010/12/24 10:18:07 asanchez Exp $
 */
package org.eclipse.ocl;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.helper.OCLHelper;
import org.eclipse.ocl.internal.OCLDebugOptions;
import org.eclipse.ocl.internal.OCLPlugin;
import org.eclipse.ocl.internal.evaluation.QueryImpl;
import org.eclipse.ocl.internal.helper.HelperUtil;
import org.eclipse.ocl.lpg.ProblemHandler;
import org.eclipse.ocl.parser.OCLAnalyzer;
import org.eclipse.ocl.parser.ValidationVisitor;
import org.eclipse.ocl.parser.backtracking.OCLBacktrackingLexer;
import org.eclipse.ocl.parser.backtracking.OCLBacktrackingParser;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.util.ObjectUtil;
import org.eclipse.ocl.utilities.ExpressionInOCL;

/**
 * 

* The Façade for parsing and evaluation of OCL documents and * constraints. The OCL is initialized with an environment factory * that supports the metamodel of the model to which the OCL constraints are to * be applied. *

*

* 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. *

*

* See the {@link Environment} class for a description of the generic type * parameters of this class. *

*

* Providers of OCL bindings for metamodels may extend this class for the * convenience of their clients, to specify the type parameter substitutions. *

* * @author Christian W. Damus (cdamus) */ public class OCL { private final EnvironmentFactory environmentFactory; private final Environment rootEnvironment; private EvaluationEnvironment evalEnv; private Map> extentMap; private List constraints = new java.util.ArrayList(); private Diagnostic problems; private Diagnostic evaluationProblems; private int parserRepairCount = 0; private boolean traceParsing = OCLPlugin .shouldTrace(OCLDebugOptions.PARSING); private boolean traceEvaluation = OCLPlugin .shouldTrace(OCLDebugOptions.EVALUATION); /** * Initializes me with an environment factory, which I will use to create a * root environment. * * @param envFactory * my environment factory */ protected OCL( EnvironmentFactory envFactory) { this(envFactory, envFactory.createEnvironment()); } /** * Initializes me with a root environment. * * @param env * my root environment */ protected OCL(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 OCL( 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 OCL( EnvironmentFactory envFactory, Environment rootEnv) { this.environmentFactory = envFactory; this.rootEnvironment = rootEnv; if (envFactory instanceof AbstractEnvironmentFactory) { AbstractEnvironmentFactory abstractFactory = (AbstractEnvironmentFactory) envFactory; abstractFactory.setEvaluationTracingEnabled(traceEvaluation); } } /** * Creates a new OCL 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 OCL newInstance( EnvironmentFactory envFactory) { return new OCL(envFactory); } /** * Creates a new OCL on the specified root environment. * * @param env * the root environment * @return the OCL façade */ public static OCL newInstance( Environment env) { return new OCL(env); } /** * Creates a new OCL 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 OCL newInstance( EnvironmentFactory envFactory, Resource resource) { return new OCL( 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; } /** * 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 extent map, if any, provided by the client to customize the * evaluation of constraints. * * @return the client-provided custom extent map, or null if * thie OCL is using the default dynamic extent map implementation */ public Map> getExtentMap() { return extentMap; } /** * 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 extentMap * a custom extent map, or null to use the default * dynamic extent map implementation */ public void setExtentMap(Map> extentMap) { this.extentMap = extentMap; } /** * Return an analyzer configured ready to parse an input string. * * @param input the input to parse * @return an analyzer * @since 3.0 */ public OCLAnalyzer createAnalyzer(String input) { OCLAnalyzer analyzer; if (parserRepairCount != 0) { OCLBacktrackingLexer lexer = new OCLBacktrackingLexer( rootEnvironment, input.toCharArray()); OCLBacktrackingParser parser = new OCLBacktrackingParser(lexer); parser.setDefaultRepairCount(parserRepairCount); lexer.lexer(parser.getIPrsStream()); analyzer = rootEnvironment.getFactory().createOCLAnalyzer(parser); } else { analyzer = rootEnvironment.getFactory().createOCLAnalyzer(rootEnvironment, input); } return analyzer; } /** * Parses an OCL document, returning the constraints parsed from it. This * OCL instance remembers these constraints; they can be * retrieved later via the {@link #getConstraints()} method. * * @param input * an OCL document as either a string or a stream * @return the constraints parsed from the document * * @throws ParserException * on failure to parse, either because of a syntactic or * semantic problem or because of an I/O failure * * @see #getConstraints() */ public List parse(OCLInput input) throws ParserException { String inputString = input.getContentAsString(); OCLAnalyzer analyzer = createAnalyzer(inputString); // clear out old diagnostics ProblemHandler ph = OCLUtil.getAdapter(rootEnvironment, ProblemHandler.class); if (ph != null) { ph.beginParse(); } List result = new java.util.ArrayList(); analyzer.parseOCLDocument(result); constraints.addAll(result); List resContents = rootEnvironment.getTypeResolver() .getResource().getContents(); for (CT ct : result) { EObject constraintEObject = (EObject) ct; if (constraintEObject.eResource() == null) { resContents.add(constraintEObject); } } if (ph != null) { ph.endParse(); try { problems = OCLUtil.checkForErrors(ph); } catch (ParserException e) { problems = e.getDiagnostic(); throw e; } } return result; } /** * 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 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 { // 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(CT constraint) throws SemanticException { // 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 * self variable). Clients should use the * {@link #isInvalid(Object)} method to check whether the evaluation result * is OclInvalid. * * @param context * the context (self) object * @param expression * the OCL expression to evaluate * * @return the value of the expression, or OclInvalid if the * evaluation fails for reasons other than a run-time exception * * @see #isInvalid(Object) * @see #check(Object, Object) */ public Object evaluate(Object context, OCLExpression 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(); localEvalEnv.add(Environment.SELF_VARIABLE_NAME, context); Map> extents = getExtentMap(); if (extents == null) { // let the evaluation environment create one extents = localEvalEnv.createExtentMap(context); } EvaluationVisitor ev = environmentFactory .createEvaluationVisitor(rootEnvironment, localEvalEnv, extents); Object result; try { result = ev.visitExpression(expression); } catch (EvaluationHaltedException e) { evaluationProblems = e.getDiagnostic(); result = rootEnvironment.getOCLStandardLibrary().getInvalid(); } finally { localEvalEnv.remove(Environment.SELF_VARIABLE_NAME); } return result; } /** * Queries whether a value is the special invalid token. This is * useful for determining whether the result of an expression evaluation is * valid. * * @param value * some OCL value * @return true if it is the invalid; * false, otherwise * * @see #evaluate(Object, OCLExpression) */ public boolean isInvalid(Object value) { return getEnvironment().getOCLStandardLibrary().getInvalid() == 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 self 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, CT constraint) { ExpressionInOCL specification = rootEnvironment .getUMLReflection().getSpecification(constraint); return check(context, specification.getBodyExpression()); } /** * 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 self 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, OCLExpression constraint) { OCLStandardLibrary stdlib = getEnvironment().getOCLStandardLibrary(); if (constraint.getType() != stdlib.getBoolean()) { throw new IllegalArgumentException("constraint is not boolean"); //$NON-NLS-1$ } Object result = evaluate(context, constraint); return Boolean.TRUE.equals(result); } /** * 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 Query} 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. *

* 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. *

* * @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 Query createQuery(OCLExpression query) { return new QueryImpl( rootEnvironment, query, extentMap); } /** * Creates a new {@link Query} 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. *

* 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. *

* * @param constraint * the OCL constraint * * @return the new query object * * @see #createQuery(OCLExpression) */ public Query createQuery(CT constraint) { return new QueryImpl( rootEnvironment, rootEnvironment.getUMLReflection() .getSpecification(constraint).getBodyExpression(), extentMap); } /** * Queries whether tracing of parsingis enabled. Tracing logs the progress * of parsing to the console, which may be of use in diagnosing problems. *

* In an Eclipse environment, tracing is also enabled by turning on the * org.eclipse.ocl/debug/parsing debug option. *

* * @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. *

* In an Eclipse environment, tracing is also enabled by turning on the * org.eclipse.ocl/debug/parsing debug option. *

* * param b whether parsing tracing is enabled * * @see #isParseTracingEnabled() */ public void setParseTracingEnabled(boolean b) { traceParsing = b; } /** *

* Queries the number of repairs to be made by the parser. *

*

* The default zero value selects use of the deterministic parser, which * terminates after one serious syntax error is detected. *

*

* A non-zero value selects the backtracking parser. The backtracking parser * may be about three times slower. *

* * @return the number of repairs to be attempted * * @see #setParserRepairCount(int) * @since 1.3 */ public int getParserRepairCount() { return parserRepairCount; } /** *

* Sets the number of repairs to be made by the parser. *

*

* The default zero value selects use of the deterministic parser, which * terminates after one serious syntax error is detected. *

*

* A non-zero value selects the backtracking parser. The backtracking parser * may be about three times slower. *

* * @param parserRepairCount * whether evaluation tracing is enabled * * @throws IllegalArgumentException * if the parserRepairCount is negative * * @see #getParserRepairCount() * @since 1.3 */ 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. *

* In an Eclipse environment, tracing is also enabled by turning on the * org.eclipse.ocl/debug/evaluation debug option. *

* * @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. *

* In an Eclipse environment, tracing is also enabled by turning on the * org.eclipse.ocl/debug/evaluation debug option. *

* * @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 null if all was OK * * @since 1.2 */ public Diagnostic getProblems() { return problems; } /** * Obtains problems, if any, occurred during evaluation of the last OCL * constraint or query expression. * * @return evaluation problems or null if all was OK * * @since 1.3 */ 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. * * @since 1.2 */ public void dispose() { // dispose of constraints by clearing their adapters for (CT 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(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy