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

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

/**
 * 
 *
 * Copyright (c) 2005, 2009, 2011 IBM Corporation, Zeligsoft Inc., 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 v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *   Zeligsoft - Bugs 238050, 253252
 *   Radek Dvorak - Bugs 261128, 265066
 *   Axel Uhl (SAP AG) - Bug 342644
 *
 * 
 *
 * $Id: AbstractEvaluationVisitor.java,v 1.12 2011/05/01 10:56:50 auhl Exp $
 */
package org.eclipse.ocl;

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

import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.internal.OCLPlugin;
import org.eclipse.ocl.internal.OCLStatusCodes;
import org.eclipse.ocl.internal.evaluation.NumberUtil;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.options.EvaluationOptions;
import org.eclipse.ocl.types.InvalidType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.UMLReflection;
import org.eclipse.ocl.utilities.Visitable;

/**
 * An evaluation visitor implementation for OCL expressions.
 * 

* Note that this class is not intended to be used or extended by * clients. Use the {@link AbstractEvaluationVisitor} interface, instead. *

*

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

* * @author Tim Klinger (tklinger) * @author Christian W. Damus (cdamus) */ public abstract class AbstractEvaluationVisitor extends AbstractVisitor implements EvaluationVisitor { // stereotypes associated with boolean-valued constraints private static Set BOOLEAN_CONSTRAINTS; private EvaluationEnvironment evalEnv; private Environment env; private OCLStandardLibrary stdlib; private Map> extentMap; private EvaluationVisitor visitor; static { BOOLEAN_CONSTRAINTS = new java.util.HashSet(); BOOLEAN_CONSTRAINTS.add(UMLReflection.INVARIANT); BOOLEAN_CONSTRAINTS.add(UMLReflection.PRECONDITION); BOOLEAN_CONSTRAINTS.add(UMLReflection.POSTCONDITION); } /** * Initializes me. * * @param env the current environment * @param evalEnv an evaluation environment (map of variable names to values) * @param extentMap a map of classes to their instance sets */ protected AbstractEvaluationVisitor( Environment env, EvaluationEnvironment evalEnv, Map> extentMap) { this.evalEnv = evalEnv; this.env = env; stdlib = env.getOCLStandardLibrary(); this.extentMap = extentMap; this.visitor = this; // assume I have no decorator } /** * Obtains the visitor on which I perform nested * {@link Visitable#accept(org.eclipse.ocl.utilities.Visitor)} calls. This * handles the case in which I am decorated by another visitor that must * intercept every visitXxx() method. If I internally just * recursively visit myself, then this decorator is cut out of the picture. * * @return my delegate visitor, which may be my own self or some other */ protected final EvaluationVisitor getVisitor() { return visitor; } /** * Sets the evaluation environment to be used by this visitor during * evaluation. * * @param evaluationEnvironment * non-null evaluation environment * @throws IllegalArgumentException * if the passed evaluationEnvironment is * null * * @since 1.3 */ protected void setEvaluationEnvironment( EvaluationEnvironment evaluationEnvironment) { if (evaluationEnvironment == null) { throw new IllegalArgumentException("null evaluation environment"); //$NON-NLS-1$ } this.evalEnv = evaluationEnvironment; } /** * Sets the visitor on which I perform nested * {@link Visitable#accept(org.eclipse.ocl.utilities.Visitor)} calls. * * @param visitor my delegate visitor * * @see #getVisitor() */ void setVisitor(EvaluationVisitor visitor) { this.visitor = visitor; } // implements the interface method public EvaluationEnvironment getEvaluationEnvironment() { return evalEnv; } // implements the interface method public Environment getEnvironment() { return env; } protected UMLReflection getUMLReflection() { return env.getUMLReflection(); } // implements the interface method public Map> getExtentMap() { return extentMap; } /** * Obtains my environment's OCL Standard Library implementation. * * @return the OCL standard library */ protected OCLStandardLibrary getStandardLibrary() { return stdlib; } /** * Obtains my environment's implementation of the OclInvalid value. * * @return the invalid result * @since 3.0 */ protected final Object getInvalid() { return getStandardLibrary().getInvalid(); } /** * Obtains my environment's implementation of the OCL Boolean type. * * @return the boolean type */ protected final C getBoolean() { return getStandardLibrary().getBoolean(); } /** * Obtains my environment's implementation of the OCL String type. * * @return the string type */ protected final C getString() { return getStandardLibrary().getString(); } /** * Obtains my environment's implementation of the OCL Integer type. * * @return the integer type */ protected final C getInteger() { return getStandardLibrary().getInteger(); } /** * Obtains my environment's implementation of the OCL UnlimitedNatural type. * * @return the unlimited natural type */ protected final C getUnlimitedNatural() { return getStandardLibrary().getUnlimitedNatural(); } /** * Obtains my environment's implementation of the OCL Real type. * * @return the real type */ protected final C getReal() { return getStandardLibrary().getReal(); } /** * Obtains the name of the specified element, if it has one. * * @param namedElement a named element * @return its name, or null if it has none */ protected String getName(Object namedElement) { return getEnvironment().getUMLReflection().getName(namedElement); } /** * This default implementation simply asks the expression to * {@linkplain Visitable#accept(org.eclipse.ocl.utilities.Visitor) accept} * me. * * @param expression an OCL expression to evaluate * * @return the result of the evaluation */ public Object visitExpression(OCLExpression expression) { try { return expression.accept(getVisitor()); } catch (EvaluationHaltedException e) { // evaluation stopped on demand, propagate further throw e; } catch (RuntimeException e) { String msg = e.getLocalizedMessage(); if (msg == null) { msg = OCLMessages.no_message; } OCLPlugin.log(Diagnostic.ERROR, OCLStatusCodes.IGNORED_EXCEPTION_WARNING, OCLMessages.bind(OCLMessages.EvaluationFailed_ERROR_, msg), e); // failure to evaluate results in invalid return getInvalid(); } } /** * This default implementation asserts that the constraint is * boolean-valued if it is an invariant, pre-condition, or post-condition * constraint and returns the value of its body expression by delegation to * {@link #visitExpression(OCLExpression)}. */ @Override public Object visitConstraint(CT constraint) { OCLExpression body = getSpecification(constraint).getBodyExpression(); boolean isBoolean = BOOLEAN_CONSTRAINTS.contains( getEnvironment().getUMLReflection().getStereotype(constraint)); if (body == null) { throw new IllegalArgumentException("constraint has no body expression"); //$NON-NLS-1$ } if (isBoolean && (body.getType() != getBoolean())) { throw new IllegalArgumentException("constraint is not boolean"); //$NON-NLS-1$ } Object result = getVisitor().visitExpression(body); return isBoolean? Boolean.TRUE.equals(result) : result; } // overrides the inherited no-op implementation @Override protected ExpressionInOCL getSpecification(CT constraint) { return getEnvironment().getUMLReflection().getSpecification(constraint); } @Override public String toString() { StringBuffer result = new StringBuffer(super.toString()); result.append(" (evaluation environment: ");//$NON-NLS-1$ result.append(getEvaluationEnvironment()); result.append(')'); return result.toString(); } /** * Convenience method to determine whether the specified value is the null * or OclInvalid. * * @param value a value * * @return whether it is undefined */ protected boolean isUndefined(Object value) { return (value == null) || (value == getEnvironment().getOCLStandardLibrary().getInvalid()); } /** * Invokes the specified additional operation on a target object. * The invocation is performed in a nested evaluation environment. * * @param operation the operation to invoke * @param body the operation's body expression * @param target the object on which to evaluate the operation body * @param args the arguments to the operation call */ protected Object call(O operation, OCLExpression body, Object target, Object[] args) { Environment myEnv = getEnvironment(); EnvironmentFactory factory = myEnv.getFactory(); // create a nested evaluation environment for this operation call EvaluationEnvironment nested = factory.createEvaluationEnvironment(getEvaluationEnvironment()); // bind "self" nested.add(Environment.SELF_VARIABLE_NAME, target); // add the parameter bindings to the local variables if (args.length > 0) { int i = 0; for (PM param : myEnv.getUMLReflection().getParameters(operation)) { nested.add(myEnv.getUMLReflection().getName(param), args[i++]); } } return factory.createEvaluationVisitor( myEnv, nested, getExtentMap()).visitExpression(body); } /** * Obtains the body of the specified operation's def or body expression, * if any. * * @param operation an operation * * @return its value expression, if any */ protected OCLExpression getOperationBody(O operation) { OCLExpression result = null; CT body = env.getDefinition(operation); if (body == null) { body = env.getBodyCondition(operation); } if (body != null) { result = env.getUMLReflection().getSpecification(body).getBodyExpression(); } return result; } /** * Obtains the body of the specified property's def or der expression, * if any. * * @param property a property * * @return its value expression, if any */ protected OCLExpression getPropertyBody(P property) { OCLExpression result = null; CT body = env.getDefinition(property); if (body == null) { body = env.getDeriveConstraint(property); } if (body != null) { result = env.getUMLReflection().getSpecification(body).getBodyExpression(); } return result; } /** * Obtains an object's value of the specified additional property. * * @param property the property to navigate * @param derivation the expression that computes its value * @param target the object in which context to evaluate the derivation * * @return the property's value */ protected Object navigate(P property, OCLExpression derivation, Object target) { Environment myEnv = getEnvironment(); EnvironmentFactory factory = myEnv.getFactory(); // create a nested evaluation environment for this property call EvaluationEnvironment nested = factory.createEvaluationEnvironment(getEvaluationEnvironment()); // bind "self" nested.add(Environment.SELF_VARIABLE_NAME, target); return factory.createEvaluationVisitor( myEnv, nested, getExtentMap()).visitExpression(derivation); } /** * Checks whether the supplied object is an instance of the supplied type. * * @param value the value to check * @param typeArg the type to check * @return true if the object is an instance of the type, false otherwise. * An error condition such as a null typeArg is * indicated by a null return value. */ protected Boolean oclIsTypeOf(Object value, Object typeArg) { @SuppressWarnings("unchecked") C type = (C) typeArg; // regardless of the source value, if the type is undefined, then so // is oclIsTypeOf if (type == null) { return null; } // the type of null is OclVoid, except that we aren't even allowed to // ask if not lax-null-handling if (value == null) { return isLaxNullHandling() // VoidType conforms to all other types except OclInvalid // See Section 8.2 of OCL 2.3 specification (10-11-42) ? Boolean.valueOf(type instanceof VoidType) : null; } // the type of invalid is OclInvalid, except that we aren't even allowed // to ask if not lax-null-handling if (value == stdlib.getInvalid()) { return isLaxNullHandling() ? Boolean.valueOf(type instanceof InvalidType) : null; } return Boolean.valueOf(getEvaluationEnvironment().isTypeOf(value, type)); } /** * Checks whether the supplied value is an instance of the supplied type or * one of its super types. * * @param value the value to check * @param typeArg the type to check * @return true iff the value is of the type or one of its super types. * An error condition such as a null typeArg is * indicated by a null return value. */ protected Boolean oclIsKindOf(Object value, Object typeArg) { @SuppressWarnings("unchecked") C type = (C) typeArg; // regardless of the source value, if the type is undefined, then so // is oclIsTypeOf if (type == null) { return null; } // OclVoid and Invalid conform to all classifiers but their instances // aren't actually useful as any type but their own. So, in case of lax // null handling, return TRUE. Otherwise, oclIsKindOf isn't even // permitted, so return null to generate an OclInvalid down the line if (value == null) { return isLaxNullHandling() ? !Boolean.valueOf(type instanceof InvalidType) : null; } if (value == stdlib.getInvalid()) { return isLaxNullHandling() ? Boolean.TRUE // OclInvalid conforms to all other types : null; } return Boolean.valueOf(getEvaluationEnvironment().isKindOf(value, type)); } /** *

* Tests whether a given number can be safely coerced to Double or * Integer without changing the value of the number. Safe means * that coercing a number to Double or Integer and then * coercing it back to the original type will result in the same value (no * loss of precision). This is trivial for types, which have a smaller * domain then Integer or Double, but for * example a Long number may not be safely coerced to * Integer. *

* If the coercion is safe, the number will be returned as either * Double or Integer, as appropriate to the original * precision. Otherwise the original number is returned. *

* * @param number a number to coerce to Integer or Double * @return the coerced number, or the original number, if coercion was not safe * * @since 1.2 */ protected Number coerceNumber(Number number) { return NumberUtil.coerceNumber(number); } /** *

* Coerces the given number to Double or Long precision, * if possible. Note that this is only impossible for BigDecimal * or BigInteger values, respectively, that are out of range of * their primitive counterparts. *

* * @param number a number to coerce to Long or Double * @return the coerced number, or the original number, in case of overflow * * @since 1.2 */ protected Number higherPrecisionNumber(Number number) { return NumberUtil.higherPrecisionNumber(number); } /** * Queries whether our evaluation environment has the option for * {@linkplain EvaluationOptions#LAX_NULL_HANDLING lax null handling} * enabled. * * @since 1.3 * @return whether lax null handling is enabled */ protected boolean isLaxNullHandling() { return EvaluationOptions.getValue(getEvaluationEnvironment(), EvaluationOptions.LAX_NULL_HANDLING); } /** * Evaluates exp. If the evaluation terminates with an * exception that does not have special semantics for the OCL evaluator * itself, such as {@link EvaluationHaltedException}, the exception is * caught, and {@link #getInvalid() invalid} is returned as the result of * the evaluation. If the evaluation terminates normally, the evaluation * result is returned.

* * Subclasses may override this method to add exceptions to the list of * exceptions that are not caught because they have special meaning to a * specialized OCL evaluator. * * @since 3.1 */ protected Object safeVisitExpression(OCLExpression exp) { Object sourceVal; try { sourceVal = exp.accept(getVisitor()); } catch (EvaluationHaltedException ehe) { throw ehe; } catch (RuntimeException e) { sourceVal = getInvalid(); } return sourceVal; } } //EvaluationVisitorImpl





© 2015 - 2024 Weber Informatics LLC | Privacy Policy