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

org.eclipse.ocl.parser.AbstractOCLAnalyzer Maven / Gradle / Ivy

/**
 * 
 *
 * Copyright (c) 2005, 2010 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 - refactored to separate from OCLAnalyzer and OCLParser
 *               - Bugs 184048, 237126, 245586, 213886, 242236, 259818, 259819, 297541, 298128
 *   Adolfo Sanchez-Barbudo Herrera - Bug 237441
 *   Zeligsoft - Bugs 243526, 243079, 245586 (merging and docs), 213886, 179990,
 *               255599, 251349, 242236, 259740
 *   Nicolas Rouquette - Bug 259818 (regression)
 *   Borland - Bug 242880
 *
 * 
 *
 * $Id: AbstractOCLAnalyzer.java,v 1.45 2011/05/01 10:56:50 auhl Exp $
 */
package org.eclipse.ocl.parser;

import static org.eclipse.ocl.Environment.SELF_VARIABLE_NAME;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.LookupException;
import org.eclipse.ocl.SemanticException;
import org.eclipse.ocl.cst.BooleanLiteralExpCS;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.cst.CSTPackage;
import org.eclipse.ocl.cst.CallExpCS;
import org.eclipse.ocl.cst.ClassifierContextDeclCS;
import org.eclipse.ocl.cst.CollectionLiteralExpCS;
import org.eclipse.ocl.cst.CollectionLiteralPartCS;
import org.eclipse.ocl.cst.CollectionRangeCS;
import org.eclipse.ocl.cst.CollectionTypeCS;
import org.eclipse.ocl.cst.CollectionTypeIdentifierEnum;
import org.eclipse.ocl.cst.ContextDeclCS;
import org.eclipse.ocl.cst.DefCS;
import org.eclipse.ocl.cst.DefExpressionCS;
import org.eclipse.ocl.cst.DerValueCS;
import org.eclipse.ocl.cst.DotOrArrowEnum;
import org.eclipse.ocl.cst.FeatureCallExpCS;
import org.eclipse.ocl.cst.IfExpCS;
import org.eclipse.ocl.cst.InitOrDerValueCS;
import org.eclipse.ocl.cst.InitValueCS;
import org.eclipse.ocl.cst.IntegerLiteralExpCS;
import org.eclipse.ocl.cst.InvCS;
import org.eclipse.ocl.cst.InvOrDefCS;
import org.eclipse.ocl.cst.InvalidLiteralExpCS;
import org.eclipse.ocl.cst.IsMarkedPreCS;
import org.eclipse.ocl.cst.IterateExpCS;
import org.eclipse.ocl.cst.IteratorExpCS;
import org.eclipse.ocl.cst.LetExpCS;
import org.eclipse.ocl.cst.LiteralExpCS;
import org.eclipse.ocl.cst.LoopExpCS;
import org.eclipse.ocl.cst.MessageExpCS;
import org.eclipse.ocl.cst.MessageExpKind;
import org.eclipse.ocl.cst.NullLiteralExpCS;
import org.eclipse.ocl.cst.OCLDocumentCS;
import org.eclipse.ocl.cst.OCLExpressionCS;
import org.eclipse.ocl.cst.OCLMessageArgCS;
import org.eclipse.ocl.cst.OperationCS;
import org.eclipse.ocl.cst.OperationCallExpCS;
import org.eclipse.ocl.cst.OperationContextDeclCS;
import org.eclipse.ocl.cst.PackageDeclarationCS;
import org.eclipse.ocl.cst.PathNameCS;
import org.eclipse.ocl.cst.PrePostOrBodyDeclCS;
import org.eclipse.ocl.cst.PrePostOrBodyEnum;
import org.eclipse.ocl.cst.PrimitiveLiteralExpCS;
import org.eclipse.ocl.cst.PrimitiveTypeCS;
import org.eclipse.ocl.cst.PropertyContextCS;
import org.eclipse.ocl.cst.RealLiteralExpCS;
import org.eclipse.ocl.cst.SimpleNameCS;
import org.eclipse.ocl.cst.SimpleTypeEnum;
import org.eclipse.ocl.cst.StringLiteralExpCS;
import org.eclipse.ocl.cst.TupleLiteralExpCS;
import org.eclipse.ocl.cst.TupleTypeCS;
import org.eclipse.ocl.cst.TypeCS;
import org.eclipse.ocl.cst.UnlimitedNaturalLiteralExpCS;
import org.eclipse.ocl.cst.VariableCS;
import org.eclipse.ocl.cst.VariableExpCS;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.expressions.CollectionLiteralExp;
import org.eclipse.ocl.expressions.CollectionLiteralPart;
import org.eclipse.ocl.expressions.CollectionRange;
import org.eclipse.ocl.expressions.EnumLiteralExp;
import org.eclipse.ocl.expressions.FeatureCallExp;
import org.eclipse.ocl.expressions.IfExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.InvalidLiteralExp;
import org.eclipse.ocl.expressions.IterateExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.LetExp;
import org.eclipse.ocl.expressions.LiteralExp;
import org.eclipse.ocl.expressions.LoopExp;
import org.eclipse.ocl.expressions.MessageExp;
import org.eclipse.ocl.expressions.NavigationCallExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StateExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralPart;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.expressions.UnspecifiedValueExp;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.lpg.AbstractAnalyzer;
import org.eclipse.ocl.lpg.BasicEnvironment2;
import org.eclipse.ocl.lpg.ProblemHandler;
import org.eclipse.ocl.options.ParsingOptions;
import org.eclipse.ocl.options.ProblemOption;
import org.eclipse.ocl.types.BagType;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.MessageType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.OrderedSetType;
import org.eclipse.ocl.types.SequenceType;
import org.eclipse.ocl.types.TypeType;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.util.CollectionUtil;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.PredefinedType;
import org.eclipse.ocl.utilities.TypedElement;
import org.eclipse.ocl.utilities.UMLReflection;

/**
 * The AbstractOCLAnalyzer supports semantic analysis of a CST
 * produced by an AbstractOCLParser to create the Essential OCL
 * AST. It is necessary that syntactic parsing and semantic analysis are
 * performed in two steps because LPG is a bottom up parser and cannot provide
 * enough contextual information to create the AST on the first pass.
 * 
 * Derived classes should extend the abstract support for EssentialOCL to full
 * support for whatever language in which EssentialOCL is embedded.
 * 
 * @since 1.2
 */
public abstract class AbstractOCLAnalyzer
		extends AbstractAnalyzer {

	/**
	 * @since 3.1
	 */
	public static final String OCL_ANNOTATIONS_URI = Environment.OCL_NAMESPACE_URI+"/Annotations"; //$NON-NLS-1$

	/** Prefix used by OCL to escape names that clash with keywords. */
	private static final String OCL_ESCAPE_PREFIX = "_"; //$NON-NLS-1$

	private static final int OCL_ESCAPE_LENGTH = OCL_ESCAPE_PREFIX.length();

	/**
	 * When a detail with this key is in an annotation with URI
	 * {@link Environment#OCL_NAMESPACE_URI} on a {@link CollectionLiteralExp},
	 * this means that the collection literal was created by the analyzer
	 * implicitly for a -> set conversion. In this case,
	 * if the single item evaluates to null it must not be
	 * added to the resulting collection by an evaluator.
	 * 
	 * @since 3.1
	 */
	public static final String IMPLICIT_SET_CONVERSION = "IMPLICIT_SET_CONVERSION"; //$NON-NLS-1$

	/*
	 * Factories for creating OCL AST nodes
	 */
	protected OCLFactory oclFactory;

	protected UMLReflection uml;

	protected final EnvironmentFactory environmentFactory;

	public AbstractOCLAnalyzer(AbstractOCLParser parser) {
		super(parser);

		Environment env = getOCLEnvironment();

		this.environmentFactory = env.getFactory();
		oclFactory = createOCLFactory(env);
		uml = env.getUMLReflection();
	}

	/**
	 * Creates/obtains the {@link OCLFactory} that I use to create OCL AST
	 * elements.
	 * 
	 * @param env
	 *            my OCL environment
	 * 
	 * @return an appropriate factory
	 */
	protected OCLFactory createOCLFactory(
			Environment env) {
		return env.getOCLFactory();
	}

	@SuppressWarnings("unchecked")
	public Environment getOCLEnvironment() {
		return getEnvironment().getAdapter(Environment.class);
	}

	/**
	 * @since 1.3
	 */
	@Override
	@SuppressWarnings("deprecation")
	public AbstractOCLParser getAbstractParser() {
		return (AbstractOCLParser) super.getParser();
	}

	@Override
	public AbstractOCLParser getParser() {
		return getAbstractParser();
	}

	protected C getBoolean() {
		return getStandardLibrary().getBoolean();
	}

	protected C getOclVoid() {
		return getStandardLibrary().getOclVoid();
	}

	protected OCLStandardLibrary getStandardLibrary() {
		return getOCLEnvironment().getOCLStandardLibrary();
	}

	/**
	 * Returns true if the token kind is an identifier or keyword, otherwise
	 * false.
	 * 
	 * @param tokenKind
	 *            the token kind to compare
	 * @return true if the token kind is an identifier or keyword, otherwise
	 *         false
	 */
	static public boolean isIdentifierOrKeyword(int tokenKind) {
		switch (tokenKind) {

			case OCLParsersym.TK_self :
			case OCLParsersym.TK_inv :
			case OCLParsersym.TK_pre :
			case OCLParsersym.TK_post :
			case OCLParsersym.TK_body :
			case OCLParsersym.TK_context :
			case OCLParsersym.TK_package :
			case OCLParsersym.TK_endpackage :
			case OCLParsersym.TK_def :
			case OCLParsersym.TK_derive :
			case OCLParsersym.TK_init :
			case OCLParsersym.TK_if :
			case OCLParsersym.TK_then :
			case OCLParsersym.TK_else :
			case OCLParsersym.TK_endif :
			case OCLParsersym.TK_and :
			case OCLParsersym.TK_or :
			case OCLParsersym.TK_xor :
			case OCLParsersym.TK_not :
			case OCLParsersym.TK_implies :
			case OCLParsersym.TK_let :
			case OCLParsersym.TK_in :
			case OCLParsersym.TK_true :
			case OCLParsersym.TK_false :
			case OCLParsersym.TK_Set :
			case OCLParsersym.TK_Bag :
			case OCLParsersym.TK_Sequence :
			case OCLParsersym.TK_Collection :
			case OCLParsersym.TK_OrderedSet :
			case OCLParsersym.TK_String :
			case OCLParsersym.TK_Integer :
			case OCLParsersym.TK_UnlimitedNatural :
			case OCLParsersym.TK_Real :
			case OCLParsersym.TK_Boolean :
			case OCLParsersym.TK_Tuple :
			case OCLParsersym.TK_OclAny :
			case OCLParsersym.TK_OclVoid :
			case OCLParsersym.TK_OclInvalid :
			case OCLParsersym.TK_OclMessage :
			case OCLParsersym.TK_null :
			case OCLParsersym.TK_invalid :
			case OCLParsersym.TK_IDENTIFIER :
			case OCLParsersym.TK_EOF_TOKEN :
				return true;

		}
		return false;
	}

	/**
	 * Constructs the string representation of an operation call.
	 * 
	 * @param operName
	 *            the operation name
	 * @param args
	 *            the arguments in the operation call
	 * 
	 * @return the string representation
	 */
	protected String operationString(
			Environment env,
			String operName, List> args) {
		StringBuffer result = new StringBuffer();

		result.append(operName);
		result.append('(');

		for (Iterator> iter = args.iterator(); iter
			.hasNext();) {

			TypedElement arg = iter.next();
			C type = arg.getType();

			result.append((type == null)
				? (String) null
				: uml.getName(type));

			if (iter.hasNext()) {
				result.append(", "); //$NON-NLS-1$
			}
		}

		result.append(')');

		return result.toString();
	}

	/**
	 * Sets the specified navigation call's qualifiers, if they are compatible
	 * with the navigated association end or association class.
	 * 
	 * @param rule
	 *            the rule name that parsed the qualifiers
	 * @param nc
	 *            the navigation call expression
	 * @param qualifiers
	 *            the qualifiers to set
	 */
	protected void setQualifiers(
			Environment env,
			String rule, NavigationCallExp nc,
			List> qualifiers) {

		if (nc instanceof PropertyCallExp) {
			P source = ((PropertyCallExp) nc).getReferredProperty();
			List

expectedQualifiers = uml.getQualifiers(source); if (expectedQualifiers.size() != qualifiers.size()) { ERROR(qualifiers, rule, OCLMessages.bind( OCLMessages.MismatchedQualifiers_ERROR_, nc.toString())); return; } else { if (!qualifiers.isEmpty()) { int iQualifierMax = expectedQualifiers.size(); for (int iQualifier = 0; iQualifier < iQualifierMax; iQualifier++) { P expectedQualifier = expectedQualifiers .get(iQualifier); OCLExpression qualifier = qualifiers.get(iQualifier); C expectedType = getOCLType(env, expectedQualifier); C qualifierType = qualifier.getType(); if (!TypeUtil.compatibleTypeMatch(env, expectedType, qualifierType)) { ERROR(qualifier, rule, OCLMessages.bind( OCLMessages.MismatchedQualifiers_ERROR_, nc .toString())); } } if (uml.isMany(source)) { C ncType = nc.getType(); if (ncType instanceof CollectionType) { // qualifying the navigation results in a // non-collection // type @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) ncType; nc.setType(ct.getElementType()); } } } } } else if (nc instanceof AssociationClassCallExp) { if (qualifiers.size() != 1) { ERROR(qualifiers, rule, OCLMessages.bind( OCLMessages.AssociationClassQualifierCount_ERROR_, nc .toString())); } if (qualifiers.isEmpty()) { // already registered error return; } Object qualifier = qualifiers.get(0); if (!(qualifier instanceof PropertyCallExp)) { ERROR(qualifier, rule, OCLMessages.bind( OCLMessages.AssociationClassQualifierType_ERROR_, nc .toString())); } else { AssociationClassCallExp acc = (AssociationClassCallExp) nc; C assocClass = acc.getReferredAssociationClass(); C sourceType = getElementType(nc.getSource().getType()); @SuppressWarnings("unchecked") P property = ((PropertyCallExp) qualifier) .getReferredProperty(); // maybe look-up didn't find a property if (property != null) { C refAssocClass = uml.getAssociationClass(property); if (refAssocClass == null) { ERROR(qualifier, rule, OCLMessages.bind( OCLMessages.AssociationClassQualifierType_ERROR_, nc.toString())); } else { if (uml.getAttributes(sourceType).contains(property) && (refAssocClass == assocClass)) { acc.setNavigationSource(property); CollectionKind kind = getCollectionKind(getOCLType( env, property)); if (kind != null) { // FIXME associate a CSTNode with the collection acc.setType(getCollectionType(null, env, kind, assocClass)); } else { acc.setType(assocClass); } } else { ERROR( qualifier, rule, OCLMessages .bind( OCLMessages.AssociationClassQualifierType_ERROR_, nc.toString())); } } } } } // all's well nc.getQualifier().addAll(qualifiers); } /** * Asserts that the specified association class is not a reflexive * association. * * @param env * the current environment * @param rule * the rule that we are matching * @param acc * the association class call expression * @param cstNode * context of the call */ protected void checkNotReflexive( Environment env, String rule, AssociationClassCallExp acc) { C assocClass = acc.getReferredAssociationClass(); List

ends; if (uml.isAssociationClass(assocClass)) { ends = uml.getMemberEnds(assocClass); } else { ends = Collections.emptyList(); } if (ends.size() == 2) { P end1 = ends.get(0); P end2 = ends.get(1); if (TypeUtil.getPropertyType(env, assocClass, end1) == TypeUtil .getPropertyType(env, assocClass, end2)) { ERROR(acc, rule, OCLMessages.bind( OCLMessages.AssociationClassAmbiguous_ERROR_, acc .toString())); } } } /** * Generate a VariableDeclaration AST node, and add it to the environment. * Variable declarations are generated for "self", let expression variables, * and iterator and iterate variables, both implicit and explicit. For * implicit variables, the name is generated by the Environment. */ protected Variable genVariableDeclaration(CSTNode cstNode, String rule, Environment env, String name, C type, OCLExpression initExp, boolean explicitFlag, boolean addToEnvironment, boolean isSelf) { Variable vdcl = oclFactory.createVariable(); initASTMapping(env, vdcl, cstNode); vdcl.setName(name); vdcl.setType(TypeUtil.resolveType(env, type)); vdcl.setInitExpression(initExp); if (addToEnvironment) { boolean result = env.addElement(name, vdcl, explicitFlag); if (!result) { if (name != null) { String message = OCLMessages.bind( OCLMessages.VariableUsed_ERROR_, name); ERROR(cstNode, rule, message); } else { ERROR(cstNode, rule, OCLMessages.VariableDeclaration_ERROR_); } } if (isSelf) { env.setSelfVariable(vdcl); } } if (addToEnvironment) { TRACE(rule, "adding variable declaration for " + vdcl.getName());//$NON-NLS-1$ } return vdcl; } /** * Generate an OperationCallExp node. operName is the input name of the * operation, which must be matched against the datatype of the operation * source. * * @param env * the current environment * @param operationCallExpCS * the operation call CST node * @param rule * the name of the concrete syntax rule that we are processing * @param operName * the operation name * @param source * the operation's source expression * @param ownerType * the type that defines the operation, in which we will look it * up. This can differ from the type of the source expression in * the case of an implicit collect iterator * @param args * the operation arguments */ protected OperationCallExp genOperationCallExp( Environment env, OperationCallExpCS operationCallExpCS, String rule, String operName, OCLExpression source, C ownerType, List> args) { OperationCallExp result; result = oclFactory.createOperationCallExp(); initASTMapping(env, result, operationCallExpCS); result.setSource(source); // Performs method signature checking O oper = lookupOperation(operationCallExpCS.getSimpleNameCS(), env, ownerType, operName, args); // sometimes we use the resolved name in case the environment's look-up // supports aliasing String resolvedName = operName; if (oper == null) { String message = OCLMessages.bind( OCLMessages.OperationNotFound_ERROR_, operationString(env, operName, args), (ownerType == null) ? null : uml.getName(ownerType)); ERROR(operationCallExpCS, rule, message); result.setType(env.getOCLStandardLibrary().getOclVoid()); } else { resolvedName = uml.getName(oper); TRACE(rule, resolvedName); result.setReferredOperation(oper); } // Set up arguments List> callargs = result.getArgument(); if (args != null) { for (OCLExpression arg : args) { if (arg == null) { ERROR(operationCallExpCS, rule, OCLMessages.BadArg_ERROR_); } else { callargs.add(arg); } } } // Compute the result type, and perform conformance checking. if (oper != null) { C resultType = null; int opcode = 0; if (TypeUtil.isStandardLibraryFeature(env, ownerType, oper)) { // the operations defined intrinsically by the standard library // are the only ones that may have opcodes opcode = OCLStandardLibraryUtil.getOperationCode(resolvedName); } else if (TypeUtil.isOclAnyOperation(env, oper)) { // source is a user class, enumeration, or data type and the // operation is defined by OclAny, not the source type opcode = OCLStandardLibraryUtil .getOclAnyOperationCode(resolvedName); } result.setOperationCode(opcode); resultType = TypeUtil.getResultType(operationCallExpCS, env, ownerType, oper, args); if (resultType == null) { resultType = getOCLType(env, oper); } // resolve collection or tuple type against the cache in the // environment resultType = TypeUtil.resolveType(env, resultType); result.setType(resultType); } return result; } /** * Analyzes a top-level document CS. * * @param documentCS * the document * @param constraints * the constraints list to populate * * @since 1.3 */ protected void documentCS(OCLDocumentCS documentCS, List constraints) { for (PackageDeclarationCS decl : documentCS.getPackageDeclarations()) { packageDeclarationCS(decl, constraints); } } /** * Analyzes a package declaration in the context of the environment created * for an {@link OCLDocumentCS}. * * @param packageDeclarationCS * the package declaration to analyze * @param env * the OCL document environment in which to analyze it * @param constraints * the constraints list to populate * * @since 1.3 */ protected void packageDeclarationCS( PackageDeclarationCS packageDeclarationCS, Environment env, List constraints) { PathNameCS pathNameCS = packageDeclarationCS.getPathNameCS(); EList pathname; Environment packageEnv; if (pathNameCS == null) { packageEnv = env; pathname = ECollections.emptyEList(); initASTMapping(packageEnv, createDummyPackage(env, packageDeclarationCS), packageDeclarationCS); } else { pathname = createSequenceOfNames(pathNameCS, null); try { packageEnv = createPackageContext(getOCLEnvironment(), pathname); if (packageEnv != null) { PK contextPackage = packageEnv.getContextPackage(); initASTMapping(packageEnv, contextPackage, packageDeclarationCS); pathNameCS.setAst(contextPackage); } } catch (LookupException e) { ERROR(pathNameCS, "packageDeclarationCS", //$NON-NLS-1$ e.getMessage()); return; } if (packageEnv == null) { ERROR(pathNameCS, "packageDeclarationCS", //$NON-NLS-1$ OCLMessages.bind(OCLMessages.PackageNotFound_ERROR_, makeString(pathname))); return; } } TRACE("packageDeclarationCS", "Package ", pathname); //$NON-NLS-2$//$NON-NLS-1$ EList contextDecls = packageDeclarationCS .getContextDecls(); for (ContextDeclCS decl : contextDecls) { contextDeclCS(decl, packageEnv, constraints); } } /** * Parses a top-level package declaration that is not nested in an * {@link OCLDocumentCS}. * * @param packageDeclarationCS * the package declaration * @param constraints * the constraints list to populate */ protected void packageDeclarationCS( PackageDeclarationCS packageDeclarationCS, List constraints) { packageDeclarationCS(packageDeclarationCS, getOCLEnvironment(), constraints); } /** * ContextDeclCS * * @param contextDeclCS * the ContextDeclCS CSTNode * @param env * the package environment * @param constraints * the constraints list to populate */ protected void contextDeclCS(ContextDeclCS contextDeclCS, Environment env, List constraints) { if (contextDeclCS instanceof OperationContextDeclCS) { operationContextDeclCS((OperationContextDeclCS) contextDeclCS, env, constraints); } else if (contextDeclCS instanceof PropertyContextCS) { propertyContextCS((PropertyContextCS) contextDeclCS, env, constraints); } else if (contextDeclCS instanceof ClassifierContextDeclCS) { classifierContextDeclCS((ClassifierContextDeclCS) contextDeclCS, env, constraints); } } /** * OperationContextDeclCS * * @param operationContextDeclCS * the OperationContextDeclCS CSTNode * @param env * the package environment * @param constraints * the constraints list to populate */ protected void operationContextDeclCS( OperationContextDeclCS operationContextDeclCS, Environment env, List constraints) { env = operationCS(operationContextDeclCS.getOperationCS(), env); if (env != null) { CT astNode; for (PrePostOrBodyDeclCS decl : operationContextDeclCS .getPrePostOrBodyDecls()) { astNode = prePostOrBodyDeclCS(env, decl); if (astNode != null) { constraints.add(astNode); } } } } /** * OperationCS * * @param operationCS * the OperationCS CSTNode * @param env * the classifier context environment * @return the operation context environment, or null if the * operation could not be resolved */ protected Environment operationCS( OperationCS operationCS, Environment env) { O operation = null; C classifier = null; EList className = createSequenceOfNames(operationCS.getPathNameCS(), null); String operationName = operationCS.getSimpleNameCS().getValue(); EList qualifiedOperationName = new BasicEList(); qualifiedOperationName.addAll(className); qualifiedOperationName.add(operationName); EList parametersCS = operationCS.getParameters(); if (className.size() > 0) { classifier = lookupClassifier(operationCS.getPathNameCS(), env, className); if (classifier != null) { // create the classifier context as parent for the operation // context env = environmentFactory.createClassifierContext(env, classifier); // ensure that the classifier context has a 'self' variable if (env.lookupLocal(SELF_VARIABLE_NAME) == null) { genVariableDeclaration(operationCS, "operationCS", env,//$NON-NLS-1$ SELF_VARIABLE_NAME, classifier, null, true, true, true); } // find the context operation List> contextParms = parametersCS(parametersCS, env); operation = lookupOperation(operationCS, env, classifier, operationName, contextParms); operationCS.getSimpleNameCS().setAst(operation); if (operation == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(qualifiedOperationName)); ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$ return null; } } } if (operation == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(qualifiedOperationName)); ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$ return null; } TypeCS operationTypeCS = operationCS.getTypeCS(); C opType1 = (operationTypeCS != null) ? typeCS(operationTypeCS, env) : getOclVoid(); C opType2 = uml.getOCLType(operation); if (!TypeUtil.compatibleTypeMatch(env, opType1, opType2)) { String message = OCLMessages.bind( OCLMessages.TypeConformanceOperation_ERROR_, makeString(qualifiedOperationName)); ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$ return null; } TRACE("operationCS", "context", qualifiedOperationName);//$NON-NLS-2$//$NON-NLS-1$ // this ensures that parameters are correctly renamed according to the // context declaration (thus ensuring that they do not mask attributes // of the context classifier) return createOperationContext(env, (OperationContextDeclCS) operationCS .eContainer(), operation); } protected Environment createOperationContext( Environment env, OperationContextDeclCS operationContextCS, O operation) { if (operationContextCS != null) { operationContextCS.setAst(operation); } // create the operation context Environment result = environmentFactory .createOperationContext(env, operation); // create a self variable for this operation if (result.lookupLocal(SELF_VARIABLE_NAME) == null) { genVariableDeclaration(operationContextCS, "operationContextDeclCS", result, //$NON-NLS-1$ SELF_VARIABLE_NAME, env.getContextClassifier(), null, true, true, true); } if (operationContextCS != null) { List contextParms = operationContextCS.getOperationCS() .getParameters(); // now, because the environment factory will have defined // variables for operation parameters, let's replace any that // are renamed by the context declaration List parms = uml.getParameters(operation); for (int i = 0; i < parms.size(); i++) { String contextParmName = contextParms.get(i).getName(); String innateParmName = uml.getName(parms.get(i)); if (!contextParmName.equals(innateParmName)) { Variable var = result.lookupLocal(innateParmName); // delete this variable result.deleteElement(innateParmName); // replace it with this one var.setName(contextParmName); result.addElement(contextParmName, var, true); } } } return result; } /** * ParametersCS * * @param parameters * the list of parameters as VariableDeclarationCS * objects * @param env * the OCL expression * @return a list of VariableDeclarations */ protected List> parametersCS(List parameters, Environment env) { return variableDeclarationListCS(parameters, env, false); } /** * PrePostOrBodyDeclCS * * @param prePostOrBodyDeclCS * the PrePostOrBodyDeclCS CSTNode * @param env * the OCL environment * @param operation * the context EOperation * @return the parsed Constraint */ protected CT prePostOrBodyDeclCS( Environment env, PrePostOrBodyDeclCS prePostOrBodyDeclCS) { ExpressionInOCL spec = createExpressionInOCL(); initASTMapping(env, spec, prePostOrBodyDeclCS, null); O operation = env.getContextOperation(); OperationContextDeclCS operationContext = (OperationContextDeclCS) prePostOrBodyDeclCS .eContainer(); // create a disposable child operation context for this environment env = createOperationContext(env, operationContext, operation); Variable selfVar = env.getSelfVariable(); spec.setContextVariable(selfVar); C operationType = getOCLType(env, operation); if (operationType instanceof VoidType) { operationType = null; // a void operation has no result } String stereotype = null; Variable resultVar = null; switch (prePostOrBodyDeclCS.getKind().getValue()) { case PrePostOrBodyEnum.PRE : stereotype = UMLReflection.PRECONDITION; break; case PrePostOrBodyEnum.POST : stereotype = UMLReflection.POSTCONDITION; // postconditions have an implicit variable "result" of the // same type as the operation if ((operationType != null) && (env.lookupLocal(Environment.RESULT_VARIABLE_NAME) == null)) { resultVar = genVariableDeclaration(null, "prePostOrBodyDeclCS0", env, //$NON-NLS-1$ Environment.RESULT_VARIABLE_NAME, operationType, null, true, true, false); initASTMapping(env, resultVar, prePostOrBodyDeclCS, null); spec.setResultVariable(resultVar); } break; case PrePostOrBodyEnum.BODY : stereotype = UMLReflection.BODY; // likewise, body conditions have an implicit variable "result" // when using the UML-style post-condition-like form of body // condition constraint if ((operationType != null) && (env.lookupLocal(Environment.RESULT_VARIABLE_NAME) == null)) { resultVar = genVariableDeclaration(null, "prePostOrBodyDeclCS", env, //$NON-NLS-1$ Environment.RESULT_VARIABLE_NAME, operationType, null, true, true, false); initASTMapping(env, resultVar, prePostOrBodyDeclCS, null); spec.setResultVariable(resultVar); } break; } CT astNode; try { OCLExpression oclExpression = oclExpressionCS( prePostOrBodyDeclCS.getExpressionCS(), env); /* * create a constraint astNode -- must reference the type of self... * also, can have a name n. type of constraint is pre/post/body... */ astNode = createConstraint(); initASTMapping(env, astNode, prePostOrBodyDeclCS); SimpleNameCS simpleNameCS = prePostOrBodyDeclCS.getSimpleNameCS(); if (simpleNameCS != null) { uml.setConstraintName(astNode, simpleNameCS.getValue()); simpleNameCS.setAst(astNode); } uml.addConstrainedElement(astNode, (EObject) operation); C owner = uml.getOwningClassifier(operation); C selfVarType = selfVar.getType(); if (owner != selfVarType) { // implicitly redefining the operation in a specializing // classifier uml.addConstrainedElement(astNode, (EObject) selfVarType); if (operationContext != null) { // check settings for using inherited feature context in // concrete syntax (note that the OLCHelper brings us in // here, too, which is why we check for the context CST) ProblemHandler.Severity sev = getEnvironment().getValue( ProblemOption.INHERITED_FEATURE_CONTEXT); if (!sev.isOK()) { getEnvironment().problem( sev, ProblemHandler.Phase.ANALYZER, OCLMessages.bind( OCLMessages.NonStd_InheritedFeatureContext_, formatQualifiedName(owner), formatName(operation)), "prePostOrBodyDeclCS", //$NON-NLS-1$ null); } } } spec.setBodyExpression(oclExpression); // compute the parameter variables List parameters = uml.getParameters(operation); Collection> vars = env.getVariables(); for (Variable var : vars) { if (parameters.contains(var.getRepresentedParameter())) { spec.getParameterVariable().add(var); initASTMapping(env, var, prePostOrBodyDeclCS); } } uml.setSpecification(astNode, spec); uml.setStereotype(astNode, stereotype); if (UMLReflection.BODY.equals(stereotype)) { env.setBodyCondition(operation, astNode); } } finally { if (resultVar != null) { // don't want this variable to linger for the next time the // environment // is used, e.g. to parse a pre-condition env.deleteElement(Environment.RESULT_VARIABLE_NAME); } } return astNode; } /** * PropertyContextCS * * @param propertyContextCS * the PropertyContextCS CSTNode * @param env * the package environment * @param constraints * the constraints list to populate * * @return the context property, or null if it could not be * resolved */ protected P propertyContextCS(PropertyContextCS propertyContextCS, Environment env, List constraints) { EList pathName = createSequenceOfNames(propertyContextCS.getPathNameCS(), null); C owner = lookupClassifier(propertyContextCS.getPathNameCS(), env, pathName); if (owner == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(pathName)); ERROR(propertyContextCS, "propertyContextCS", message);//$NON-NLS-1$ return null; } owner = uml.asOCLType(owner); // create the classifier context as parent for the property context env = environmentFactory.createClassifierContext(env, owner); SimpleNameCS simplenameCS = propertyContextCS.getSimpleNameCS(); String simpleName = simplenameCS.getValue(); P property = lookupProperty(simplenameCS, env, owner, simpleName); propertyContextCS.setAst(property); EList propertyName = new BasicEList(); propertyName.addAll(pathName); propertyName.add(simpleName); if (property == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(propertyName)); ERROR(propertyContextCS, "propertyContextCS", message);//$NON-NLS-1$ return null; } C type = typeCS(propertyContextCS.getTypeCS(), env); C propertyType = getPropertyType(simplenameCS, env, owner, property); if ((type == null) || !TypeUtil.exactTypeMatch(env, propertyType, type)) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(propertyName)); ERROR(propertyContextCS.getTypeCS(), "propertyContextCS", message);//$NON-NLS-1$ } TRACE("propertyContextCS", "context", propertyName); //$NON-NLS-2$//$NON-NLS-1$ // create the property context env = environmentFactory.createAttributeContext(env, property); InitValueCS initCS = null; DerValueCS derCS = null; for (InitOrDerValueCS initOrDerValueCS : propertyContextCS.getConstraints()) { if (initOrDerValueCS instanceof InitValueCS) { if (initCS != null) { String message = OCLMessages.bind( OCLMessages.PropertyConstraints_ERROR_, makeString(propertyName)); ERROR(initOrDerValueCS, "propertyContextCS", message);//$NON-NLS-1$ } else { initCS = (InitValueCS) initOrDerValueCS; CT astNode = initOrDerValueCS(env, initOrDerValueCS); constraints.add(astNode); } } else if (initOrDerValueCS instanceof DerValueCS) { if (derCS != null) { String message = OCLMessages.bind( OCLMessages.PropertyConstraints_ERROR_, makeString(propertyName)); ERROR(initOrDerValueCS, "propertyContextCS", message);//$NON-NLS-1$ } else { derCS = (DerValueCS) initOrDerValueCS; CT astNode = initOrDerValueCS(env, initOrDerValueCS); constraints.add(astNode); } } } return property; } protected Environment createPropertyContext( Environment env, PropertyContextCS propertyContextCS, P property) { // create the classifier context as parent for the property context Environment result = environmentFactory .createAttributeContext(env, property); // ensure that the classifier context has a 'self' variable if (result.lookupLocal(SELF_VARIABLE_NAME) == null) { genVariableDeclaration( propertyContextCS, "propertyContextCS", result, //$NON-NLS-1$ SELF_VARIABLE_NAME, env.getContextClassifier(), null, true, true, true); } return result; } /** * InitOrDerValueCS * * @param initOrDerValueCS * the InitOrDerValueCS CSTNode * @param env * the OCL environment * @param property * the context EStructuralFeature * @return the parsed Constraint */ protected CT initOrDerValueCS( Environment env, InitOrDerValueCS initOrDerValueCS) { P property = env.getContextProperty(); PropertyContextCS propertyContext = null; for (EObject container = initOrDerValueCS.eContainer(); container != null; container = container .eContainer()) { if (container instanceof PropertyContextCS) { propertyContext = (PropertyContextCS) container; break; } } // create a disposable property context for this environment env = createPropertyContext(env, propertyContext, property); String stereotype = null; switch (initOrDerValueCS.eClass().getClassifierID()) { case CSTPackage.INIT_VALUE_CS : stereotype = UMLReflection.INITIAL; break; case CSTPackage.DER_VALUE_CS : stereotype = UMLReflection.DERIVATION; break; } OCLExpression oclExpression = oclExpressionCS(initOrDerValueCS .getExpressionCS(), env); CT astNode = createConstraint(); initASTMapping(env, astNode, initOrDerValueCS); uml.addConstrainedElement(astNode, (EObject) property); C owner = uml.getOwningClassifier(property); if (owner != env.getSelfVariable().getType()) { // implicitly redefining the property in a specializing classifier uml.addConstrainedElement(astNode, (EObject) env.getSelfVariable().getType()); if (propertyContext != null) { // check settings for using inherited feature context in // concrete syntax (note that the OLCHelper brings us in // here, too, which is why we check for the context CST) ProblemHandler.Severity sev = getEnvironment().getValue( ProblemOption.INHERITED_FEATURE_CONTEXT); if (!sev.isOK()) { getEnvironment().problem( sev, ProblemHandler.Phase.ANALYZER, OCLMessages.bind( OCLMessages.NonStd_InheritedFeatureContext_, formatQualifiedName(owner), formatName(property)), "initOrDerValueCS", //$NON-NLS-1$ null); } } } ExpressionInOCL spec = createExpressionInOCL(); initASTMapping(env, spec, initOrDerValueCS, null); spec.setBodyExpression(oclExpression); spec.setContextVariable(env.getSelfVariable()); uml.setSpecification(astNode, spec); uml.setStereotype(astNode, stereotype); if (UMLReflection.DERIVATION.equals(stereotype)) { env.setDeriveConstraint(property, astNode); } else { env.setInitConstraint(property, astNode); } return astNode; } /** * ClassifierContextDeclCS * * @param classifierContextDeclCS * the ClassifierContextDeclCS CSTNode * @param env * the package environment * @param constraints * the constraints list to populate * * @param the * classifier context environment, or null of the * classifier could not be resolved */ protected Environment classifierContextDeclCS( ClassifierContextDeclCS classifierContextDeclCS, Environment env, List constraints) { Environment result = null; PathNameCS pathNameCS = classifierContextDeclCS.getPathNameCS(); EList pathName = createSequenceOfNames(pathNameCS, null); C type = lookupClassifier(pathNameCS, env, pathName); if (type == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedContext_ERROR_, makeString(pathName)); ERROR(classifierContextDeclCS, "classifierContextDeclCS", message);//$NON-NLS-1$ // return null; type = createDummyInvalidType(env, pathNameCS, pathNameCS .toString()); } type = uml.asOCLType(type); result = environmentFactory.createClassifierContext(env, type); classifierContextDeclCS.setAst(type); if (result.getSelfVariable() == null) { // ensure that the classifier context has a "self" variable genVariableDeclaration(classifierContextDeclCS, "classifierContextDeclCS", result, //$NON-NLS-1$ SELF_VARIABLE_NAME, type, null, true, true, true); } TRACE("classifierContextDeclCS", "context", pathName); //$NON-NLS-2$//$NON-NLS-1$ for (InvOrDefCS decl : classifierContextDeclCS.getConstraints()) { CT constraint = invOrDefCS(decl, result); if (constraint != null) { constraints.add(constraint); } } return result; } protected Environment createClassifierContext( Environment env, ClassifierContextDeclCS classifierContextDeclCS, C classifier) { // create the classifier context as parent for the property context Environment result = environmentFactory .createClassifierContext(env, classifier); // ensure that the classifier context has a 'self' variable if (result.lookupLocal(SELF_VARIABLE_NAME) == null) { genVariableDeclaration(classifierContextDeclCS, "propertyContextCS", result, //$NON-NLS-1$ SELF_VARIABLE_NAME, env.getContextClassifier(), null, true, true, true); } return result; } /** * InvOrDefCS * * @param invOrDefCS * the InvOrDefCS CSTNode * @param env * the OCL environment * @return the parsed Constraint */ protected CT invOrDefCS(InvOrDefCS invOrDefCS, Environment env) { C classifier = env.getContextClassifier(); // create a disposable classifier context for this environment env = createClassifierContext(env, (ClassifierContextDeclCS) invOrDefCS .eContainer(), classifier); CT astNode = null; if (invOrDefCS instanceof InvCS) { astNode = invCS((InvCS) invOrDefCS, env); } else if (invOrDefCS instanceof DefCS) { astNode = defCS((DefCS) invOrDefCS, env); } return astNode; } /** * InvCS * * @param invCS * the InvCS CSTNode * @param env * the OCL environment * @return the parsed Constraint */ protected CT invCS(InvCS invCS, Environment env) { OCLExpression oclExpression = oclExpressionCS(invCS .getExpressionCS(), env); CT astNode = createConstraint(); SimpleNameCS simpleNameCS = invCS.getSimpleNameCS(); if (simpleNameCS != null) { uml.setConstraintName(astNode, simpleNameCS.getValue()); } C type = env.getContextClassifier(); uml.addConstrainedElement(astNode, (EObject) type); ExpressionInOCL spec = createExpressionInOCL(); initASTMapping(env, astNode, invCS); initASTMapping(env, env.getSelfVariable(), invCS, null); initASTMapping(env, spec, invCS.getExpressionCS(), null); spec.setBodyExpression(oclExpression); spec.setContextVariable(env.getSelfVariable()); if (simpleNameCS != null) { simpleNameCS.setAst(spec.getContextVariable()); } uml.setSpecification(astNode, spec); uml.setStereotype(astNode, UMLReflection.INVARIANT); return astNode; } /** * DefCS * * @param defCS * the DefCS CSTNode * @param env * the OCL environment * @return the parsed Constraint */ protected CT defCS(DefCS defCS, Environment env) { Environment contextEnv; DefExpressionCS defExpr = defCS.getDefExpressionCS(); EObject feature = null; OCLExpression expression = null; CT astNode = createConstraint(); Variable variable = null; C operType = null; C contextClassifier = env.getContextClassifier(); SimpleNameCS simpleNameCS = defCS.getSimpleNameCS(); if (simpleNameCS != null) { uml.setConstraintName(astNode, simpleNameCS.getValue()); } uml.addConstrainedElement(astNode, (EObject) contextClassifier); ExpressionInOCL spec = createExpressionInOCL(); initASTMapping(env, astNode, defCS); initASTMapping(env, env.getSelfVariable(), defCS, null); initASTMapping(env, spec, defExpr); if (simpleNameCS != null) { simpleNameCS.setAst(spec.getContextVariable()); } uml.setSpecification(astNode, spec); uml.setStereotype(astNode, UMLReflection.DEFINITION); if (defExpr != null) { try { if (defExpr.getVariableCS() != null) { // context of the expression is the classifier contextEnv = env; variable = variableDeclarationCS(defExpr.getVariableCS(), contextEnv, false); spec.setResultVariable(variable); P existing = lookupProperty(null, env, contextClassifier, variable.getName()); if (existing != null) { ERROR(defCS, "defCS", //$NON-NLS-1$ OCLMessages.bind( OCLMessages.DuplicateProperty_ERROR_, variable .getName(), uml.getName(contextEnv .getContextClassifier()))); } spec.setContextVariable(env.getSelfVariable()); // define the property now, so that recursive references to // it will resolve correctly feature = (EObject) env.defineAttribute(contextClassifier, variable, astNode); if (getEnvironment().getValue( ParsingOptions.DEFINITION_CONSTRAINS_FEATURE)) { uml.addConstrainedElement(astNode, feature); } expression = oclExpressionCS(defExpr.getExpressionCS(), contextEnv); } else if (defExpr.getOperationCS() != null) { // context of the expression is the new operation OperationCS operCS = defExpr.getOperationCS(); contextEnv = environmentFactory.createEnvironment(env); List> params = variableDeclarationListCS( operCS.getParameters(), contextEnv, true); operType = typeCS(operCS.getTypeCS(), contextEnv); String operName = operCS.getSimpleNameCS().getValue(); O existing = lookupOperation(null, env, contextClassifier, operName, params); if (existing != null) { ERROR(defCS, "defCS", //$NON-NLS-1$ OCLMessages .bind(OCLMessages.DuplicateOperation_ERROR_, operationString(env, operName, params), uml .getName(contextEnv .getContextClassifier()))); } spec.setContextVariable(env.getSelfVariable()); spec.getParameterVariable().addAll(params); // define the operation now, so that recursive references to // it will resolve correctly feature = (EObject) env.defineOperation(contextClassifier, operName, operType, params, astNode); operCS.setAst(feature); operCS.getSimpleNameCS().setAst(feature); if (operCS.getPathNameCS() != null) operCS.getPathNameCS().setAst(contextClassifier); if (getEnvironment().getValue( ParsingOptions.DEFINITION_CONSTRAINS_FEATURE)) { uml.addConstrainedElement(astNode, feature); } expression = oclExpressionCS(defExpr.getExpressionCS(), contextEnv); } if ((feature != null) && defCS.isStatic()) { Boolean supportStatic = getEnvironment().getValue( ParsingOptions.SUPPORT_STATIC_FEATURES); if (!supportStatic) { ERROR(defCS, "defCS", //$NON-NLS-1$ OCLMessages.bind( OCLMessages.UnsupportedStatic_ERROR_, null)); } else if (!uml.setIsStatic(feature, true)) { ERROR(defCS, "defCS", //$NON-NLS-1$ OCLMessages.bind( OCLMessages.UnimplementedStatic_ERROR_, null)); } } if ((feature != null) && (expression != null)) { C featureType = getOCLType(env, feature); C bodyType = expression.getType(); if ((featureType == null) || !TypeUtil.compatibleTypeMatch(env, bodyType, featureType)) { expression = null; // trigger undefinition of the // feature astNode = null; // fail to return an AST node String message = OCLMessages.bind( OCLMessages.DefinitionConstraintConformance_ERROR_, uml.getName(bodyType), uml.getName(featureType)); ERROR(defCS, "defCS", message);//$NON-NLS-1$ } spec.setBodyExpression(expression); } } finally { if ((feature != null) && (expression == null)) { // failed to parse the body expression? Undefine the feature env.undefine(feature); } } } return astNode; } /** * VariableDeclarationCS * * @param variableDeclarationCS * the VariableDeclarationCS CSTNode * @param env * the OCL environment * @param addToEnvironment * boolean whether or not to add the the parsed variable to the * environment * @return the parsed VariableDeclaration */ protected Variable variableDeclarationCS( VariableCS variableDeclarationCS, Environment env, boolean addToEnvironment) { String varName = variableDeclarationCS.getName(); C type = null; if (variableDeclarationCS.getTypeCS() != null) { type = typeCS(variableDeclarationCS.getTypeCS(), env); } OCLExpression expr = null; if (variableDeclarationCS.getInitExpression() != null) { expr = oclExpressionCS(variableDeclarationCS.getInitExpression(), env); } // handle the generic typing of OclMessages if (expr != null) { C exprType = expr.getType(); while (exprType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) exprType; exprType = ct.getElementType(); } if (exprType instanceof MessageType) { C varType = type; if (varType instanceof CollectionType) { do { @SuppressWarnings("unchecked") CollectionType collType = (CollectionType) varType; varType = collType.getElementType(); if (varType == env.getOCLStandardLibrary() .getOclMessage()) { // substitute the actual type for the generic type collType.setElementType(exprType); break; } } while (varType instanceof CollectionType); } else if (type == env.getOCLStandardLibrary().getOclMessage()) { // substitute the actual type for the generic type type = exprType; } } } Variable astNode = genVariableDeclaration(variableDeclarationCS, "variableDeclarationCS", env, varName, type, expr, //$NON-NLS-1$ true, addToEnvironment, false); initStartEndPositions(astNode, variableDeclarationCS); if (variableDeclarationCS.getTypeCS() != null) { initTypePositions(astNode, variableDeclarationCS.getTypeCS()); } return astNode; } /** * VariableDeclarationListCS * * @param variableDeclarationCS * list of VariableDeclarationCSs * @param env * the OCL environment * @param addToEnvironment * boolean whether or not to add the the parsed variable to the * environment * @return list of VariableDeclarations */ protected List> variableDeclarationListCS( List variableDeclarationCS, Environment env, boolean addToEnvironment) { List> variableDeclarations = new java.util.ArrayList>(); for (VariableCS next : variableDeclarationCS) { variableDeclarations.add(variableDeclarationCS(next, env, addToEnvironment)); } return variableDeclarations; } /** * TypeCS * * @param typeCS * the TypeCS CSTNode * @param env * the OCL environment * @return an EClassifier representing the type */ protected C typeCS(TypeCS typeCS, Environment env) { C astNode = null; if (typeCS instanceof PrimitiveTypeCS) { astNode = primitiveTypeCS(((PrimitiveTypeCS) typeCS).getType(), env); typeCS.setAst(astNode); } else if (typeCS instanceof PathNameCS) { EList pathName = createSequenceOfNames((PathNameCS) typeCS, null); astNode = lookupClassifier(typeCS, env, pathName); if (astNode == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedType_ERROR_, formatPath(pathName)); ERROR(typeCS, "typeCS", message);//$NON-NLS-1$ astNode = createDummyInvalidType(env, typeCS, message); } typeCS.setAst(astNode); } else if (typeCS instanceof CollectionTypeCS || typeCS instanceof TupleTypeCS) { if (typeCS instanceof CollectionTypeCS) { astNode = collectionTypeCS((CollectionTypeCS) typeCS, env); } else if (typeCS instanceof TupleTypeCS) { astNode = tupleTypeCS((TupleTypeCS) typeCS, env); } } return astNode; } /** * stateExpCS * * @param source * the source expression of the oclIsInState operation * @param stateExpCS * the StateExpCS CSTNode * @param env * the OCL environment * @return a StateExp representing the state * @since 3.0 */ protected StateExp stateExpCS(OCLExpression source, CSTNode stateExpCS, EList statePath, Environment env) { C sourceType = null; if (source != null) { sourceType = source.getType(); } S state = null; if (!statePath.isEmpty()) { // to support content-assist, we can parse an expression that // has no state, to provide suggestions for the first part // of the name. Validation of the expression will assert // the presence of some referred state state = lookupState(stateExpCS, env, sourceType, statePath); if (state == null) { ERROR(stateExpCS, "stateExpCS", //$NON-NLS-1$ OCLMessages.bind(OCLMessages.NoSuchState_ERROR_, statePath, sourceType == null ? null : uml.getName(sourceType))); // FIXME Set state to some unresolvedState to avoid NPEs etc } } StateExp astNode = oclFactory.createStateExp(); initASTMapping(env, astNode, stateExpCS); astNode.setReferredState(state); astNode.setType(env.getOCLStandardLibrary().getState()); astNode.setName(makeName(statePath)); initStartEndPositions(astNode, stateExpCS); return astNode; } /** * CollectionTypeCS * * @param collectionTypeCS * the CollectionTypeCS CSTNode * @param env * the OCL environment * @return an EClassifier representing the collection type */ protected C collectionTypeCS(CollectionTypeCS collectionTypeCS, Environment env) { CollectionKind kind = collectionTypeIdentifierCS(collectionTypeCS .getCollectionTypeIdentifier()); C type = typeCS(collectionTypeCS.getTypeCS(), env); C result = getCollectionType(collectionTypeCS, env, kind, type); @SuppressWarnings("unchecked") CollectionType astNode = (CollectionType) result; initTypePositions(astNode, collectionTypeCS.getTypeCS()); return result; } /** * CollectionTypeIdentifierCS * * @param collectionTypeIdentifier * the CollectionTypeIdentifierEnum representing the * collection type * @return the parsed CollectionType */ protected CollectionKind collectionTypeIdentifierCS( CollectionTypeIdentifierEnum collectionTypeIdentifier) { CollectionKind astNode = null; switch (collectionTypeIdentifier.getValue()) { case CollectionTypeIdentifierEnum.SET : astNode = CollectionKind.SET_LITERAL; TRACE("collectionTypeIdentifierCS", "SET");//$NON-NLS-2$//$NON-NLS-1$ break; case CollectionTypeIdentifierEnum.BAG : astNode = CollectionKind.BAG_LITERAL; TRACE("collectionTypeIdentifierCS", "BAG"); //$NON-NLS-2$//$NON-NLS-1$ break; case CollectionTypeIdentifierEnum.SEQUENCE : astNode = CollectionKind.SEQUENCE_LITERAL; TRACE("collectionTypeIdentifierCS", "SEQUENCE"); //$NON-NLS-2$//$NON-NLS-1$ break; case CollectionTypeIdentifierEnum.COLLECTION : astNode = CollectionKind.COLLECTION_LITERAL; TRACE("collectionTypeIdentifierCS", "COLLECTION"); //$NON-NLS-2$//$NON-NLS-1$ break; case CollectionTypeIdentifierEnum.ORDERED_SET : astNode = CollectionKind.ORDERED_SET_LITERAL; TRACE("collectionTypeIdentifierCS", "ORDERED_SET"); //$NON-NLS-2$//$NON-NLS-1$ break; } return astNode; } /** * TupleTypeCS * * @param tupleTypeCS * the TupleTypeCS CSTNode * @param env * the OCL environment * @return the parsed TupleTypeCS */ protected C tupleTypeCS(TupleTypeCS tupleTypeCS, Environment env) { Set names = new HashSet(); String nodeName = null; EList> vdcls = new BasicEList>(); String name; List> variableDeclarations = variableDeclarationListCS( tupleTypeCS.getVariables(), env, false); for (Variable vdcl : variableDeclarations) { vdcls.add(vdcl); name = vdcl.getName(); TRACE("tupleTypeCS", " name = " + name);//$NON-NLS-2$//$NON-NLS-1$ if (names.contains(name)) { String message = OCLMessages.bind( OCLMessages.DuplicateNameInTuple_ERROR_, name); ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$ vdcl.setName(null); } else { names.add(name); } if (vdcl.getInitExpression() != null) { String message = OCLMessages.bind( OCLMessages.InitExpNotAllowed_ERROR_, name); ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$ } if (vdcl.getType() == null) { String message = OCLMessages.bind( OCLMessages.DeclarationType_ERROR_, name); ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$ vdcl.setType(getOclVoid()); } if (nodeName == null) { nodeName = "Tuple("; //$NON-NLS-1$ } else { nodeName += ", "; //$NON-NLS-1$ } nodeName += vdcl.getName() + ":" + uml.getName(vdcl.getType()); //$NON-NLS-1$ } return getTupleType(tupleTypeCS, env, vdcls); } /** * OCLExpressionCS * * @param oclExpressionCS * the OCLExpressionCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression oclExpressionCS(OCLExpressionCS oclExpressionCS, Environment env) { OCLExpression astNode = null; if (oclExpressionCS instanceof IfExpCS) { astNode = ifExpCS((IfExpCS) oclExpressionCS, env); } else if (oclExpressionCS instanceof CallExpCS) { astNode = propertyCallExpCS((CallExpCS) oclExpressionCS, env); } else if (oclExpressionCS instanceof VariableExpCS) { astNode = variableExpCS((VariableExpCS) oclExpressionCS, env); } else if (oclExpressionCS instanceof LiteralExpCS) { astNode = literalExpCS((LiteralExpCS) oclExpressionCS, env); } else if (oclExpressionCS instanceof LetExpCS) { astNode = letExp((LetExpCS) oclExpressionCS, env); } else if (oclExpressionCS instanceof MessageExpCS) { astNode = messageExpCS((MessageExpCS) oclExpressionCS, env); } if (astNode != null) { astNode.setType(TypeUtil.resolveType(env, astNode.getType())); initStartEndPositions(astNode, oclExpressionCS); } return astNode; } /** * VariableExpCS * * @param variableExpCS * the VariableExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression variableExpCS(VariableExpCS variableExpCS, Environment env) { SimpleNameCS simpleNameCS = variableExpCS.getSimpleNameCS(); OCLExpression astNode = simpleNameCS(simpleNameCS, env, null); variableExpCS.setAst(astNode); List> qualifiers = qualifiersCS(variableExpCS .getArguments(), env, astNode); if (isAtPre(variableExpCS)) { if (astNode instanceof FeatureCallExp) { ((FeatureCallExp) astNode).setMarkedPre(true); } else { ERROR(variableExpCS.getIsMarkedPreCS(), "variableExpCS", OCLMessages.IllegalAtPre_ERROR_);//$NON-NLS-1$ } } if (!qualifiers.isEmpty()) { if (astNode instanceof NavigationCallExp) { @SuppressWarnings("unchecked") NavigationCallExp callNode = (NavigationCallExp) astNode; setQualifiers(env, "variableExpCS", callNode, qualifiers); //$NON-NLS-1$ } else if ((astNode instanceof LoopExp) && ((LoopExp) astNode).getBody() instanceof NavigationCallExp) { // might have parsed an implicit collect expression @SuppressWarnings("unchecked") NavigationCallExp callNode = (NavigationCallExp) ((LoopExp) astNode) .getBody(); setQualifiers(env, "variableExpCS", callNode, qualifiers);//$NON-NLS-1$ } else { ERROR(variableExpCS, "variableExpCS", //$NON-NLS-1$ OCLMessages.bind(OCLMessages.IllegalQualifiers_ERROR_, computeInputString(variableExpCS))); } } else if (astNode instanceof AssociationClassCallExp) { @SuppressWarnings("unchecked") AssociationClassCallExp callNode = (AssociationClassCallExp) astNode; checkNotReflexive(env, "variableExpCS", callNode);//$NON-NLS-1$ } return astNode; } /** * Queries whether the specified call expression is adorned with * {@literal @pre}. * * @param callExp * a call expression * * @return whether the expression is marked pre * * @since 1.3 */ protected boolean isAtPre(FeatureCallExpCS callExp) { IsMarkedPreCS atPre = callExp.getIsMarkedPreCS(); return atPre != null; } /** * Queries whether the specified variable expression is adorned with * {@literal @pre}. * * @param variableExp * a variable expression * * @return whether the expression is marked pre * * @since 1.3 */ protected boolean isAtPre(VariableExpCS callExp) { IsMarkedPreCS atPre = callExp.getIsMarkedPreCS(); return atPre != null; } /** * Creates a variable expression with the variable that it references. * * @param env * the current parsing environment * @param cst * the concrete syntax that produces the variable expression * @param var * the referred variable * * @return the variable expression * @since 1.3 */ protected VariableExp createVariableExp( Environment env, CSTNode cst, Variable var) { VariableExp result = oclFactory.createVariableExp(); initASTMapping(env, result, cst, var); if (var != null) { result.setType(var.getType()); result.setReferredVariable(var); result.setName(var.getName()); } return result; } /** * QualifiersCS * * @param arguments * the OCLExpressionCS arguments list * @param env * the OCL environment * @param navigation * @return the parsed OCLExpressions list */ protected List> qualifiersCS( List arguments, Environment env, OCLExpression navigation) { if (arguments.isEmpty()) { return Collections.emptyList(); } List> qualifiers = new java.util.ArrayList>(); if (navigation instanceof LoopExp) { @SuppressWarnings("unchecked") LoopExp loopNode = (LoopExp) navigation; navigation = loopNode.getBody(); } if (navigation instanceof AssociationClassCallExp) { @SuppressWarnings("unchecked") AssociationClassCallExp acc = (AssociationClassCallExp) navigation; OCLExpression source = acc.getSource(); OCLExpressionCS arg = arguments.get(0); if (!(arg instanceof VariableExpCS)) { ERROR(arg, "qualifiersCS",//$NON-NLS-1$ OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_, "VariableExpCS",//$NON-NLS-1$ arg.eClass().getName())); } else { SimpleNameCS qualifier = ((VariableExpCS) arg) .getSimpleNameCS(); String simpleName = qualifier.getValue(); C sourceType = source != null ? source.getType() : null; P property = lookupProperty(qualifier, env, sourceType, simpleName); if (property == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedVar_ERROR_, simpleName); ERROR(sourceType, "qualifiersCS", message);//$NON-NLS-1$ } else { TRACE("qualifierCS", "Reference: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$ PropertyCallExp ref = oclFactory .createPropertyCallExp(); initASTMapping(env, ref, arg); ref.setReferredProperty(property); ref.setType(getPropertyType(qualifier, env, sourceType, property)); if (source == null) { Variable implicitSource = env .lookupImplicitSourceForProperty(simpleName); VariableExp src = createVariableExp(env, arg, implicitSource); ref.setSource(src); } initStartEndPositions(ref, qualifier); initPropertyPositions(ref, qualifier); qualifiers.add(ref); } } } else { for (OCLExpressionCS arg : arguments) { OCLExpression qualExp = oclExpressionCS(arg, env); if (qualExp != null) { qualifiers.add(qualExp); } } } return qualifiers; } /** * IfExpCS * * @param ifExpCS * the IfExpCS CSTNode * @param env * the OCL environment * @return the parsed IfExpCS */ protected IfExp ifExpCS(IfExpCS ifExpCS, Environment env) { OCLExpression condition = oclExpressionCS(ifExpCS.getCondition(), env); OCLExpression thenExpression = oclExpressionCS(ifExpCS .getThenExpression(), env); OCLExpression elseExpression = oclExpressionCS(ifExpCS .getElseExpression(), env); TRACE("ifExpCS", " "); //$NON-NLS-2$//$NON-NLS-1$ IfExp astNode = oclFactory.createIfExp(); if (isErrorNode(condition)) { // don't validate the condition type } else if (condition.getType() != getBoolean()) { ERROR(ifExpCS.getCondition(), "ifExpCS", OCLMessages.bind( //$NON-NLS-1$ OCLMessages.BooleanForIf_ERROR_, computeInputString(ifExpCS .getCondition()))); } initASTMapping(env, astNode, ifExpCS); astNode.setCondition(condition); astNode.setThenExpression(thenExpression); astNode.setElseExpression(elseExpression); if ((thenExpression != null) && (elseExpression != null)) { C commonType = getCommonSuperType(ifExpCS, "ifExpCS", env, //$NON-NLS-1$ thenExpression.getType(), elseExpression.getType()); astNode.setType(commonType); if (isErrorNode(thenExpression)) { // propagate error stigma to the if expression markAsErrorNode(astNode); } if (isErrorNode(elseExpression)) { // propagate error stigma to the if expression markAsErrorNode(astNode); } } else { astNode.setType(getOclVoid()); } initStartEndPositions(astNode, ifExpCS); return astNode; } /** * LetExpCS * * @param letExpCS * the LetExpCS CSTNode * @param env * the OCL environment * @return the parsed LetExpCS * * @deprecated Since 1.3, use {@link #letExp(LetExpCS, Environment)}, * instead. */ @SuppressWarnings("unchecked") @Deprecated protected LetExp letExpCS(LetExpCS letExpCS, Environment env) { return (LetExp) letExp(letExpCS, env); } /** * LetExpCS * * @param letExpCS * the LetExpCS CSTNode * @param env * the OCL environment * * @return the parsed LetExpCS * * @since 1.3 */ protected OCLExpression letExp(LetExpCS letExpCS, Environment env) { return letExpCSRecursive(letExpCS, 0, env); } /** * Constructs the LetExp * * @param letExpCS * the LetExpCS CSTNode * @param index * the index of the VariableDeclarationCS to parse * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression letExpCSRecursive(LetExpCS letExpCS, int index, Environment env) { OCLExpression astNode; if (index < letExpCS.getVariables().size()) { VariableCS variableDeclarationCS = letExpCS.getVariables().get( index); Variable variableDeclaration = variableDeclarationCS( variableDeclarationCS, env, true); String varName = variableDeclaration.getName(); if (variableDeclaration.getType() == null) { String message = OCLMessages.bind( OCLMessages.DeclarationType_ERROR_, varName); ERROR(variableDeclarationCS, "letExpCS", message);//$NON-NLS-1$ variableDeclaration.setType(getOclVoid()); } if (variableDeclaration.getInitExpression() == null) { String message = OCLMessages.bind( OCLMessages.DeclarationNoInitExp_ERROR_, varName); ERROR(variableDeclarationCS, "letExpCS", message);//$NON-NLS-1$ } OCLExpression letSubExp = letExpCSRecursive(letExpCS, ++index, env); LetExp letExp = oclFactory.createLetExp(); initASTMapping(env, letExp, letExpCS); letExp.setVariable(variableDeclaration); letExp.setIn(letSubExp); if (letSubExp != null) { letExp.setType(letSubExp.getType()); } else { letExp.setType(getOclVoid()); } astNode = letExp; env.deleteElement(varName); } else { astNode = oclExpressionCS(letExpCS.getInExpression(), env); } return astNode; } /** * Parses a simpleNameCS token. This method is largely a * template, delegating to helpers that may be separately overridden * to resolve simple names to various kinds of expression. * * @param simpleNameCS * the simpleNameCS CSTNode * @param env * the OCL environment * @param source * the source of the simpleNameCS * @return the parsed OCLExpression * * @see #simpleAssociationClassName(SimpleNameCS, Environment, * OCLExpression, Object, String) * @see #simplePropertyName(SimpleNameCS, Environment, OCLExpression, * Object, String) * @see #simpleTypeName(SimpleNameCS, Environment, OCLExpression, Object, * String) * @see #simpleVariableName(SimpleNameCS, Environment, OCLExpression, * String) * @see #simpleUndefinedName(SimpleNameCS, Environment, OCLExpression, * String) */ protected OCLExpression simpleNameCS(SimpleNameCS simpleNameCS, Environment env, OCLExpression source) { if ((source != null) && isErrorNode(source)) { // don't attempt to parse navigation from an unparseable source return source; // return the same unparseable token } String simpleName = null; C classifier = null; /* * A name can be a variable defined by a Variable declaration, the * special variable "self", an attribute or a reference to another * object. If the source is not null, then the last token was a "." or * "->" The source is used to establish the navigation. If no type is * provided, then either the name is a the use of a variable, or there * is an implicit variable declaration (self or an iterator) that is the * source. */ switch (simpleNameCS.getType().getValue()) { case SimpleTypeEnum.SELF : case SimpleTypeEnum.KEYWORD : case SimpleTypeEnum.IDENTIFIER : simpleName = simpleNameCS.getValue(); break; case SimpleTypeEnum.INTEGER : case SimpleTypeEnum.STRING : case SimpleTypeEnum.REAL : case SimpleTypeEnum.BOOLEAN : case SimpleTypeEnum.OCL_ANY : case SimpleTypeEnum.OCL_VOID : case SimpleTypeEnum.OCL_INVALID : case SimpleTypeEnum.OCL_MESSAGE : case SimpleTypeEnum.UNLIMITED_NATURAL : // if we have a source, then this is a feature call if (source == null) { classifier = primitiveTypeCS(simpleNameCS.getType(), env); simpleName = uml.getName(classifier); } break; } /* * The source may be a collection type (for example, in * self.children.name, children may be a set.)_ In this case, we have to * get the element type of children, so that the attribute name can be * found. The source type can also be a tuple type. In this case, we * need to get the EClass of the tuple. */ C sourceElementType = null; if (source != null) { sourceElementType = source.getType(); if (sourceElementType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) sourceElementType; sourceElementType = ct.getElementType(); } } // cascaded alternatives for a simpleNameCS OCLExpression astNode = simpleTypeName(simpleNameCS, env, source, classifier, simpleName); if (astNode == null) { astNode = simpleVariableName(simpleNameCS, env, source, simpleName); } if (astNode == null) { astNode = simpleNavigationName(simpleNameCS, env, source, sourceElementType, simpleName); } if (astNode == null) { astNode = simpleAssociationClassName(simpleNameCS, env, source, sourceElementType, simpleName); } if (astNode == null) { astNode = simpleUndefinedName(simpleNameCS, env, source, simpleName); } /* * If the source type is a collection, then need there is an implicit * COLLECT operator. Note that this rule is not called after "->". Check * for FeatureCallExp in case we created a dummy InvalidLiteralExp. */ if ((source != null) && (source.getType() instanceof CollectionType) && (astNode instanceof FeatureCallExp)) { astNode = createImplicitCollect(source, (FeatureCallExp) astNode, env, simpleNameCS); } return astNode; } /** * Attempts to parse a simpleNameCS as an association-class call * expression. * * @param simpleNameCS * the simple name * @param env * the current environment * @param source * the navigation source expression, or null if the * source is implicit * @param owner * the owner of the association-class end to be navigated, or * null if the source is implicit * @param simpleName * the simple name, as a string * @return the parsed association-class call, or null if the * simple name does not resolve to a related association class * * @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression) * * @since 1.3 */ protected AssociationClassCallExp simpleAssociationClassName( SimpleNameCS simpleNameCS, Environment env, OCLExpression source, C owner, String simpleName) { AssociationClassCallExp result = null; C assocClass = lookupAssociationClassReference(simpleNameCS, env, owner, simpleName); if (assocClass != null) { TRACE("variableExpCS", "Association class: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$ result = oclFactory.createAssociationClassCallExp(); initASTMapping(env, result, simpleNameCS); result.setReferredAssociationClass(assocClass); if (source != null) { result.setSource(source); } else { Variable implicitSource = env .lookupImplicitSourceForAssociationClass(simpleName); VariableExp src = createVariableExp(env, simpleNameCS, implicitSource); result.setSource(src); } // infer the navigation source and type of the association class // call expression from the association class end that is // implicitly navigated (in case it is not explicit as a qualifier) C acrefType = null; C sourceType = getElementType(result.getSource().getType()); List

ends = uml.getMemberEnds(assocClass); List

available = uml.getAttributes(sourceType); for (P end : ends) { if (available.contains(end)) { // this is the navigation source result.setNavigationSource(end); CollectionKind kind = getCollectionKind(getOCLType(env, end)); if (kind != null) { acrefType = getCollectionType(simpleNameCS, env, kind, assocClass); } else { acrefType = assocClass; } } } if (acrefType == null) { // for non-navigable association classes, assume set type acrefType = getSetType(simpleNameCS, env, assocClass); } result.setType(acrefType); initPropertyPositions(result, simpleNameCS); } return result; } /** * Allows subclasses to return calls that are not necessarily a {@link PropertyCallExp} but some * other {@link NavigationCallExp}. * * @since 3.1 */ protected NavigationCallExp simpleNavigationName( SimpleNameCS simpleNameCS, Environment env, OCLExpression source, C owner, String simpleName) { return simplePropertyName(simpleNameCS, env, source, owner, simpleName); } /** * Attempts to parse a simpleNameCS as a property call expression. * * @param simpleNameCS * the simple name * @param env * the current environment * @param source * the navigation source expression, or null if the * source is implicit * @param owner * the owner of the property to be navigated, or * null if the source is implicit * @param simpleName * the simple name, as a string * @return the parsed property call, or null if the simple name * does not resolve to an available property * * @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression) * * @since 1.3 */ protected PropertyCallExp simplePropertyName( SimpleNameCS simpleNameCS, Environment env, OCLExpression source, C owner, String simpleName) { if (simpleName == null) { return null; } PropertyCallExp result = null; P property = lookupProperty(simpleNameCS, env, owner, simpleName); if (property != null) { TRACE("variableExpCS", "Property: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$ result = oclFactory.createPropertyCallExp(); initASTMapping(env, result, simpleNameCS, null); result.setReferredProperty(property); result.setType(getPropertyType(simpleNameCS, env, owner, property)); if (source != null) { result.setSource(source); } else { Variable implicitSource = env .lookupImplicitSourceForProperty(simpleName); VariableExp src = createVariableExp(env, simpleNameCS, implicitSource); result.setSource(src); } initPropertyPositions(result, simpleNameCS); } return result; } /** * Attempts to parse a simpleNameCS as a type expression. For the * sake of completeness and generality, information about a navigation * source is provided, if any. In such cases, it is usually * inappropriate to attempt to resolve the simple name as a type expression. * Also, the referenced classifier may already be determined to * be a member of the OCL Standard Library. * * @param simpleNameCS * the simple name * @param env * the current environment * @param source * the navigation source expression, if any, in which case this * would not be a type expression in OCL * @param classifier * the referenced classifier, if it is already known to be one of * the OCL Standard Library types * @param simpleName * the simple name, as a string * @return the parsed type expression, or null if the simple * name does not resolve to an accessible type * * @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression) * * @since 1.3 */ protected TypeExp simpleTypeName(SimpleNameCS simpleNameCS, Environment env, OCLExpression source, C classifier, String simpleName) { if (simpleNameCS.getAst() != null) { // Non-null when invoked from variableExpCS, so there is // no point trying to turn the variable name into a type return null; } TypeExp result = null; // if we have a source, then this is a feature call if ((classifier == null) && (source == null)) { classifier = lookupClassifier(simpleNameCS, env, Collections .singletonList(simpleName)); } if (classifier != null) { /* * Variable is a classifier. Classifiers are used in allInstances, * isKindOf, isTypeOf, asTypeOf operations */ result = typeCS(simpleNameCS, env, classifier); } return result; } /** * Attempts to parse a simpleNameCS as a variable expression. The * navigation source, if any, is provided for completeness and * generality. If there is a navigation source, then it is not usually * appropriate to attempt to resolve the simple name as a variable. * * @param simpleNameCS * the simple name * @param env * the current environment * @param source * the navigation source expression, if any, in which case this * would not be a variable expression in OCL * @param simpleName * the simple name, as a string * @return the parsed variable call, or null if the simple name * does not resolve to an accessible variable * * @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression) * * @since 1.3 */ protected VariableExp simpleVariableName(SimpleNameCS simpleNameCS, Environment env, OCLExpression source, String simpleName) { VariableExp result = null; if (source == null) { Variable vdcl = env.lookup(simpleName); if (vdcl != null) { // Either a use of a declared variable or self TRACE("variableExpCS", "Variable Expression: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$ result = createVariableExp(env, simpleNameCS, vdcl); } } return result; } /** * The error case for simpleNameCS, which is called when the name * cannot be resolved to any suitable expression. The result is a * {@linkplain #createDummyInvalidLiteralExp(Environment, CSTNode) dummy} * expression. * * @param simpleNameCS * the simple name * @param env * the current environment * @param source * the navigation source expression, or null if the * source is implicit * @param simpleName * the simple name, as a string * @return the dummy expression that is a placeholder for the unresolved * reference * * @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression) * * @since 1.3 */ protected OCLExpression simpleUndefinedName(SimpleNameCS simpleNameCS, Environment env, OCLExpression source, String simpleName) { if ((source != null) && (env.lookup(simpleName)) != null) { String message = OCLMessages.bind(OCLMessages.VarInNavExp_ERROR_, simpleName); ERROR(simpleNameCS, "variableExpCS", message);//$NON-NLS-1$ } else { String message = OCLMessages.bind( OCLMessages.UnrecognizedVar_ERROR_, simpleName); ERROR(simpleNameCS, "variableExpCS", message);//$NON-NLS-1$ } return createDummyInvalidLiteralExp(env, simpleNameCS); } /** * Creates an implicit collect iterator expression for a * property call on a collection-type source expression. * * @param source * the property call source expression * @param propertyCall * the property call expression * @param env * the current environment * * @return the collect expression */ protected IteratorExp createImplicitCollect(OCLExpression source, FeatureCallExp propertyCall, Environment env, CSTNode cstNode) { @SuppressWarnings("unchecked") C sourceElementType = ((CollectionType) source.getType()) .getElementType(); IteratorExp result = oclFactory.createIteratorExp(); initASTMapping(env, result, cstNode, null); Variable itervar = genVariableDeclaration(cstNode, "modelPropertyCallCS", env,//$NON-NLS-1$ null, sourceElementType, null, false, true, false); List> iters = result.getIterator(); iters.add(itervar); result.setBody(propertyCall); result.setName("collect");//$NON-NLS-1$ VariableExp vexp = createVariableExp(env, cstNode, itervar); /* * adjust the source variable for the body expression to be the newly * generated implicit iterator variable */ propertyCall.setSource(vexp); if (!(propertyCall instanceof OperationCallExp)) { // the overall start and end positions are the property positions propertyCall.setStartPosition(propertyCall .getPropertyStartPosition()); propertyCall.setEndPosition(propertyCall.getPropertyEndPosition()); } result.setSource(source); // the result of a collect() is flattened, so if the value // that we are collecting is a Collection type, the resulting // type must be flattened by taking its element type (recursively) C bodyType = propertyCall.getType(); if (bodyType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) bodyType; bodyType = CollectionUtil.getFlattenedElementType(ct); } if (source.getType() instanceof SequenceType || source.getType() instanceof OrderedSetType) { C c = getCollectionType(cstNode, env, CollectionKind.SEQUENCE_LITERAL, bodyType); result.setType(c); } else { C c = getCollectionType(cstNode, env, CollectionKind.BAG_LITERAL, bodyType); result.setType(c); } env.deleteElement(itervar.getName()); return result; } /** * PrimitiveTypeCS * * @param simpleType * the SimpleTypeEnum representing the primitive * type * @return an EClassifie representing the primitive type */ protected C primitiveTypeCS(SimpleTypeEnum simpleType, Environment env) { C astNode = null; switch (simpleType) { case INTEGER_LITERAL : astNode = env.getOCLStandardLibrary().getInteger(); break; case UNLIMITED_NATURAL_LITERAL : astNode = env.getOCLStandardLibrary().getUnlimitedNatural(); break; case STRING_LITERAL : astNode = env.getOCLStandardLibrary().getString(); break; case REAL_LITERAL : astNode = env.getOCLStandardLibrary().getReal(); break; case BOOLEAN_LITERAL : astNode = env.getOCLStandardLibrary().getBoolean(); break; case OCL_ANY_LITERAL : astNode = env.getOCLStandardLibrary().getOclAny(); break; case OCL_VOID_LITERAL : astNode = env.getOCLStandardLibrary().getOclVoid(); break; case OCL_INVALID_LITERAL : astNode = env.getOCLStandardLibrary().getOclInvalid(); break; case OCL_MESSAGE_LITERAL : astNode = env.getOCLStandardLibrary().getOclMessage(); break; } return astNode; } /** * PrimitiveLiteralExpCS * * @param primitiveLiteralExpCS * the PrimitiveLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed LiteralExp */ protected LiteralExp primitiveLiteralExpCS( PrimitiveLiteralExpCS primitiveLiteralExpCS, Environment env) { LiteralExp astNode = null; if (primitiveLiteralExpCS instanceof IntegerLiteralExpCS) { astNode = integerLiteralExpCS( (IntegerLiteralExpCS) primitiveLiteralExpCS, env); } else if (primitiveLiteralExpCS instanceof UnlimitedNaturalLiteralExpCS) { astNode = unlimitedNaturalLiteralExpCS( (UnlimitedNaturalLiteralExpCS) primitiveLiteralExpCS, env); } else if (primitiveLiteralExpCS instanceof RealLiteralExpCS) { astNode = realLiteralExpCS( (RealLiteralExpCS) primitiveLiteralExpCS, env); } else if (primitiveLiteralExpCS instanceof StringLiteralExpCS) { astNode = stringLiteralExpCS( (StringLiteralExpCS) primitiveLiteralExpCS, env); } else if (primitiveLiteralExpCS instanceof BooleanLiteralExpCS) { astNode = booleanLiteralExpCS( (BooleanLiteralExpCS) primitiveLiteralExpCS, env); } return astNode; } /** * IntegerLiteralExpCS * * @param integerLiteralExpCS * the IntegerLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed IntegerLiteralExp */ protected IntegerLiteralExp integerLiteralExpCS( IntegerLiteralExpCS integerLiteralExpCS, Environment env) { IntegerLiteralExp astNode = oclFactory.createIntegerLiteralExp(); initASTMapping(env, astNode, integerLiteralExpCS); astNode.setIntegerSymbol(integerLiteralExpCS.getIntegerSymbol()); astNode.setType(env.getOCLStandardLibrary().getInteger()); TRACE( "integerLiteralExpCS", "Integer: " + integerLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * UnlimitedNaturalLiteralExpCS * * @param unlimitedNaturalLiteralExpCS * the UnlimitedNaturalLiteralExpCS * CSTNode * @param env * the OCL environment * @return the parsed UnlimitedNaturalLiteralExp */ protected UnlimitedNaturalLiteralExp unlimitedNaturalLiteralExpCS( UnlimitedNaturalLiteralExpCS unlimitedNaturalLiteralExpCS, Environment env) { UnlimitedNaturalLiteralExp astNode = oclFactory .createUnlimitedNaturalLiteralExp(); initASTMapping(env, astNode, unlimitedNaturalLiteralExpCS); astNode.setIntegerSymbol(unlimitedNaturalLiteralExpCS .getIntegerSymbol()); astNode.setType(env.getOCLStandardLibrary().getUnlimitedNatural()); TRACE( "unlimitedNaturalLiteralExpCS", "UnlimitedNatural: " + unlimitedNaturalLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * RealLiteralExpCS * * @param realLiteralExpCS * the RealLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed RealLiteralExp */ protected RealLiteralExp realLiteralExpCS( RealLiteralExpCS realLiteralExpCS, Environment env) { RealLiteralExp astNode = oclFactory.createRealLiteralExp(); initASTMapping(env, astNode, realLiteralExpCS); astNode.setRealSymbol(realLiteralExpCS.getRealSymbol()); astNode.setType(env.getOCLStandardLibrary().getReal()); TRACE("realLiteralExpCS", "Real: " + realLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * StringLiteralExpCS * * @param stringLiteralExpCS * the StringLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed StringLiteralExp */ protected StringLiteralExp stringLiteralExpCS( StringLiteralExpCS stringLiteralExpCS, Environment env) { StringLiteralExp astNode = oclFactory.createStringLiteralExp(); initASTMapping(env, astNode, stringLiteralExpCS); astNode.setStringSymbol(stringLiteralExpCS.getUnescapedStringSymbol()); astNode.setType(env.getOCLStandardLibrary().getString()); TRACE("stringLiteralExpCS", "String: " + stringLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * BooleanLiteralExpCS * * @param booleanLiteralExpCS * the BooleanLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed BooleanLiteralExp */ protected BooleanLiteralExp booleanLiteralExpCS( BooleanLiteralExpCS booleanLiteralExpCS, Environment env) { BooleanLiteralExp astNode = oclFactory.createBooleanLiteralExp(); initASTMapping(env, astNode, booleanLiteralExpCS); astNode.setBooleanSymbol(booleanLiteralExpCS.getBooleanSymbol()); astNode.setType(env.getOCLStandardLibrary().getBoolean()); TRACE( "booleanLiteralExpCS", "Boolean: " + booleanLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * NullLiteralExpCS * * @param nullLiteralExpCS * the NullLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed NullLiteralExp */ protected NullLiteralExp nullLiteralExpCS( NullLiteralExpCS nullLiteralExpCS, Environment env) { NullLiteralExp astNode = oclFactory.createNullLiteralExp(); initASTMapping(env, astNode, nullLiteralExpCS); astNode.setType(env.getOCLStandardLibrary().getOclVoid()); TRACE("nullLiteralExpCS", "OclVoid: null");//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * InvalidLiteralExpCS * * @param invalidLiteralExpCS * the InvalidLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed InvalidLiteralExp */ protected InvalidLiteralExp invalidLiteralExpCS( InvalidLiteralExpCS invalidLiteralExpCS, Environment env) { InvalidLiteralExp astNode = oclFactory.createInvalidLiteralExp(); initASTMapping(env, astNode, invalidLiteralExpCS); astNode.setType(env.getOCLStandardLibrary().getOclInvalid()); TRACE("invalidLiteralExpCS", "Invalid: invalid");//$NON-NLS-2$//$NON-NLS-1$ return astNode; } /** * LiteralExpCS * * @param literalExpCS * the LiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed LiteralExp */ protected OCLExpression literalExpCS(LiteralExpCS literalExpCS, Environment env) { OCLExpression astNode = null; if (literalExpCS instanceof PrimitiveLiteralExpCS) { astNode = primitiveLiteralExpCS( (PrimitiveLiteralExpCS) literalExpCS, env); } else if (literalExpCS instanceof CollectionLiteralExpCS) { astNode = collectionLiteralExpCS( (CollectionLiteralExpCS) literalExpCS, env); } else if (literalExpCS instanceof TupleLiteralExpCS) { astNode = tupleLiteralExpCS((TupleLiteralExpCS) literalExpCS, env); // } else if (literalExpCS instanceof EnumLiteralExpCS) { // astNode = enumLiteralExpCS((EnumLiteralExpCS) literalExpCS, env); } else if (literalExpCS instanceof NullLiteralExpCS) { astNode = nullLiteralExpCS((NullLiteralExpCS) literalExpCS, env); } else if (literalExpCS instanceof InvalidLiteralExpCS) { astNode = invalidLiteralExpCS((InvalidLiteralExpCS) literalExpCS, env); } return astNode; } /** * TupleLiteralExpCS * * @param tupleLiteralExpCS * the TupleLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed TupleLiteralExp */ protected TupleLiteralExp tupleLiteralExpCS( TupleLiteralExpCS tupleLiteralExpCS, Environment env) { TupleLiteralExp astNode; HashSet names = new HashSet(); String nodeName = null; String name; astNode = oclFactory.createTupleLiteralExp(); initASTMapping(env, astNode, tupleLiteralExpCS); EList> tupleParts = astNode.getPart(); TRACE("tupleLiteralExpCS", "Tuple");//$NON-NLS-2$//$NON-NLS-1$ EList tupleLiteralPartListCS = tupleLiteralExpCS .getVariables(); EList> tupleLiteralParts = tupleLiteralPartListCS( tupleLiteralPartListCS, env); for (TupleLiteralPart part : tupleLiteralParts) { tupleParts.add(part); name = part.getName(); TRACE("tupleLiteralExpCS", " name = " + name);//$NON-NLS-2$//$NON-NLS-1$ if (names.contains(name)) { String message = OCLMessages.bind( OCLMessages.DuplicateNameInTuple_ERROR_, name); ERROR(part, "tupleLiteralPartCS", message);//$NON-NLS-1$ part.setName(null); } else { names.add(name); } if (part.getValue() == null) { String message = OCLMessages.bind( OCLMessages.MissingTypeInTupleLiteralPart_ERROR_, name, computeInputString(tupleLiteralExpCS)); ERROR(tupleLiteralExpCS, "tupleLiteralExpCS", message);//$NON-NLS-1$ } if (part.getType() == null) { if (part.getValue() != null) { // type is implied from init expression part.setType(part.getValue().getType()); } else { part.setType(getOclVoid()); } } if (nodeName == null) { nodeName = "Tuple{"; //$NON-NLS-1$ } else { nodeName += ", "; //$NON-NLS-1$ } nodeName += part.getName() + ":" + uml.getName(part.getType()); //$NON-NLS-1$ } C tt = getTupleType(tupleLiteralExpCS, env, tupleParts); astNode.setType(tt); for (TupleLiteralPart part : tupleParts) { // don't need to worry about ambiguity in the tuple type definition part.setAttribute(env.lookupProperty(tt, part.getName())); } return astNode; } /** * tupleLiteralPartListCS * * @param variableDeclarations * list of VariableDeclarationCSes * @param env * the OCL environment * * @return list of TupleLiteralParts */ protected EList> tupleLiteralPartListCS( List variableDeclarations, Environment env) { EList> result = new BasicEList>(); for (Iterator i = variableDeclarations.iterator(); i .hasNext();) { result.add(tupleLiteralPartCS(i.next(), env)); } return result; } /** * tupleLiteralPartCS * * @param variableDeclarationCS * the VariableDeclarationCS CSTNode * @param env * the OCL environment * @param addToEnvironment * boolean whether or not to add the the parsed variable to the * environment * @return the parsed VariableDeclaration */ protected TupleLiteralPart tupleLiteralPartCS( VariableCS variableDeclarationCS, Environment env) { String varName = variableDeclarationCS.getName(); C type = null; if (variableDeclarationCS.getTypeCS() != null) { type = typeCS(variableDeclarationCS.getTypeCS(), env); } OCLExpression expr = null; if (variableDeclarationCS.getInitExpression() != null) { expr = oclExpressionCS(variableDeclarationCS.getInitExpression(), env); } TupleLiteralPart astNode = oclFactory.createTupleLiteralPart(); initASTMapping(env, astNode, variableDeclarationCS); astNode.setName(varName); astNode.setType(type); astNode.setValue(expr); initStartEndPositions(astNode, variableDeclarationCS); if (variableDeclarationCS.getTypeCS() != null) { initTypePositions(astNode, variableDeclarationCS.getTypeCS()); } if ((expr != null) && isErrorNode(expr)) { // propagate error stigma to the tuple literal markAsErrorNode(astNode); } return astNode; } /** * EnumLiteralExpCS * * @param enumLiteralExpCS * the EnumLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed EnumLiteralExp */ protected TypeExp typeCS(CSTNode cstNode, Environment env, C type) { TypeExp astNode = oclFactory.createTypeExp(); initASTMapping(env, astNode, cstNode, null); astNode.setReferredType(type); astNode.setType(getTypeType(cstNode, env, type)); return astNode; } /** * CollectionLiteralExpCS * * @param collectionLiteralExpCS * the CollectionLiteralExpCS CSTNode * @param env * the OCL environment * @return the parsed CollectionLiteralExp */ protected CollectionLiteralExp collectionLiteralExpCS( CollectionLiteralExpCS collectionLiteralExpCS, Environment env) { CollectionLiteralExp astNode; astNode = null; CollectionKind kind = null; CollectionLiteralPart collectionLiteralPartExp = null; List> collectionParts = null; C type = null; C resultType = null; kind = collectionTypeIdentifierCS(collectionLiteralExpCS .getCollectionType()); astNode = oclFactory.createCollectionLiteralExp(); initASTMapping(env, astNode, collectionLiteralExpCS); astNode.setKind(kind); collectionParts = astNode.getPart(); EList collectionLiteralPartsCS = collectionLiteralExpCS .getCollectionLiteralParts(); if (!collectionLiteralPartsCS.isEmpty()) { Iterator i = collectionLiteralPartsCS .iterator(); CollectionLiteralPartCS colPart = i.next(); collectionLiteralPartExp = collectionLiteralPartCS(colPart, env); collectionParts.add(collectionLiteralPartExp); type = collectionLiteralPartExp.getType(); if (isErrorNode(collectionLiteralPartExp)) { // propagate error stigma to the collection literal markAsErrorNode(astNode); } while (i.hasNext()) { collectionLiteralPartExp = collectionLiteralPartCS(i.next(), env); C type1 = collectionLiteralPartExp.getType(); type = getCommonSuperType(colPart, "collectionLiteralExpCS", env, type, type1); //$NON-NLS-1$ collectionParts.add(collectionLiteralPartExp); if (isErrorNode(collectionLiteralPartExp)) { // propagate error stigma to the collection literal markAsErrorNode(astNode); } } } if (collectionParts.isEmpty()) { // absolute wildcard element type resultType = getCollectionType(collectionLiteralExpCS, env, kind, env.getOCLStandardLibrary().getOclVoid()); } else { resultType = getCollectionType(collectionLiteralExpCS, env, kind, type); } astNode.setType(resultType); return astNode; } /** * CollectionLiteralPartCS * * @param collectionLiteralPartCS * the CollectionLiteralPartCS CSTNode * @param env * the OCL environment * @return the parsed CollectionLiteralPart */ protected CollectionLiteralPart collectionLiteralPartCS( CollectionLiteralPartCS collectionLiteralPartCS, Environment env) { CollectionLiteralPart astNode; astNode = null; OCLExpression expr1 = null; OCLExpression expr2 = null; CollectionRange collRange = null; CollectionItem collItem = null; expr1 = oclExpressionCS(collectionLiteralPartCS.getExpressionCS(), env); if (collectionLiteralPartCS instanceof CollectionRangeCS) { CollectionRangeCS collectionRangeCS = (CollectionRangeCS) collectionLiteralPartCS; expr2 = oclExpressionCS(collectionRangeCS.getLastExpressionCS(), env); collRange = oclFactory.createCollectionRange(); initASTMapping(env, collRange, collectionLiteralPartCS); collRange.setFirst(expr1); collRange.setLast(expr2); collRange.setType(expr1.getType()); if (expr1.getType() != expr2.getType()) { ERROR(collectionLiteralPartCS, "collectionLiteralPartCS", //$NON-NLS-1$ OCLMessages.bind(OCLMessages.FirstLastTypeMismatch_ERROR_, computeInputString(collectionLiteralPartCS))); } astNode = collRange; if (isErrorNode(expr1) || isErrorNode(expr2)) { // propagate error stigma to the collection literal part markAsErrorNode(astNode); } TRACE("collectionLiteralPartCS", "collection range");//$NON-NLS-2$//$NON-NLS-1$ } else { collItem = oclFactory.createCollectionItem(); initASTMapping(env, collItem, collectionLiteralPartCS); collItem.setType(expr1.getType()); collItem.setItem(expr1); astNode = collItem; if (isErrorNode(expr1)) { // propagate error stigma to the collection literal part markAsErrorNode(astNode); } TRACE("collectionLiteralPartCS", "collection item");//$NON-NLS-2$//$NON-NLS-1$ } return astNode; } /** * PropertyCallExpCS * * @param propertyCallExpCS * the PropertyCallExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression propertyCallExpCS(CallExpCS propertyCallExpCS, Environment env) { OCLExpression astNode = null; if (propertyCallExpCS instanceof LoopExpCS) { astNode = loopExpCS((LoopExpCS) propertyCallExpCS, env); } else if (propertyCallExpCS instanceof FeatureCallExpCS) { // FIXME enumLiteral astNode = modelPropertyCallExpCS( (FeatureCallExpCS) propertyCallExpCS, env); } initStartEndPositions(astNode, propertyCallExpCS); return astNode; } /** * LoopExpCS * * @param loopExpCS * the LoopExpCS CSTNode * @param env * the OCL environment * @return the parsed LoopExp */ protected LoopExp loopExpCS(LoopExpCS loopExpCS, Environment env) { LoopExp astNode = null; if (loopExpCS instanceof IteratorExpCS) { astNode = iteratorExpCS((IteratorExpCS) loopExpCS, env); } else if (loopExpCS instanceof IterateExpCS) { astNode = iterateExpCS((IterateExpCS) loopExpCS, env); } return astNode; } /** * OCLExpressionCS * * @param oclExpressionCS * the OCLExpressionCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression getCollectionSourceExpression( OCLExpressionCS oclExpressionCS, Environment env) { OCLExpression astNode = oclExpressionCS(oclExpressionCS, env); if (astNode == null) { astNode = createDummyInvalidLiteralExp(env, oclExpressionCS); } /* * The source must be a collection type. */ if (!(astNode.getType() instanceof CollectionType)) { CollectionLiteralExp astNode1 = oclFactory .createCollectionLiteralExp(); initASTMapping(env, astNode1, oclExpressionCS, null); astNode1.setKind(CollectionKind.SET_LITERAL); if (astNode1 instanceof EModelElement) { // add an annotation indicating to the evaluator that // this collection literal was created from an implicit // -> conversion and therefore single null items shall // not be added EAnnotation implicitSetConversionAnnotation = ((EModelElement) astNode1) .getEAnnotation(OCL_ANNOTATIONS_URI); if (implicitSetConversionAnnotation == null) { implicitSetConversionAnnotation = EcoreFactory.eINSTANCE .createEAnnotation(); implicitSetConversionAnnotation.setSource(OCL_ANNOTATIONS_URI); ((EModelElement) astNode1).getEAnnotations().add(implicitSetConversionAnnotation); } implicitSetConversionAnnotation.getDetails().put(IMPLICIT_SET_CONVERSION, "true"); //$NON-NLS-1$ } List> collectionParts = astNode1.getPart(); CollectionItem collItem = oclFactory.createCollectionItem(); initASTMapping(env, collItem, oclExpressionCS, null); collItem.setType(astNode.getType()); collItem.setItem(astNode); collectionParts.add(collItem); C type = getCollectionType(oclExpressionCS, env, astNode1.getKind(), astNode.getType()); astNode1.setType(type); if (isErrorNode(astNode)) { // propagate error mark to the collection literal markAsErrorNode(astNode1); } astNode = astNode1; } return astNode; } /** * IteratorExpCS * * @param iteratorExpCS * the IteratorExpCS CSTNode * @param env * the OCL environment * @return the parsed IteratorExp */ protected IteratorExp iteratorExpCS(IteratorExpCS iteratorExpCS, Environment env) { OCLExpression source = getCollectionSourceExpression(iteratorExpCS .getSource(), env); if (source == null) { return null; } String name = iteratorExpCS.getSimpleNameCS().getValue(); Variable vdcl = null; Variable vdcl1 = null; IteratorExp astNode = oclFactory.createIteratorExp(); initASTMapping(env, astNode, iteratorExpCS); astNode.setName(name); resolveIteratorOperation(iteratorExpCS.getSimpleNameCS(), env); List> iterators = astNode.getIterator(); if (iteratorExpCS.getVariable1() != null) { vdcl = variableDeclarationCS(iteratorExpCS.getVariable1(), env, true); if (vdcl.getType() == null) { C sourceType = source.getType(); if (sourceType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) sourceType; vdcl.setType(ct.getElementType()); } } iterators.add(vdcl); if (iteratorExpCS.getVariable2() != null) { vdcl1 = variableDeclarationCS(iteratorExpCS.getVariable2(), env, true); if (vdcl1.getType() == null) { C sourceType = source.getType(); if (sourceType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) sourceType; vdcl1.setType(ct.getElementType()); } } iterators.add(vdcl1); } } else { // Synthesize the iterator expression. @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source.getType(); vdcl = genVariableDeclaration(iteratorExpCS, "iteratorExpCS", env, null, //$NON-NLS-1$ ct.getElementType(), null, false, true, false); iterators.add(vdcl); } OCLExpressionCS exprCS = iteratorExpCS.getBody(); OCLExpression expr = null; if (isErrorNode(source)) { // don't attempt to parse iterator body for an unparseable source expr = createDummyInvalidLiteralExp(env, iteratorExpCS); // don't parse call expressions sourced on this result markAsErrorNode(astNode); } else { expr = oclExpressionCS(exprCS, env); } TRACE("oclIteratorExpCS: ", name);//$NON-NLS-1$ if (name.equals("forAll") || name.equals("exists") || name.equals("one") || name.equals("isUnique")) {//$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ astNode.setType(env.getOCLStandardLibrary().getBoolean()); } else if (name.equals("select") || name.equals("reject")) {//$NON-NLS-2$//$NON-NLS-1$ astNode.setType(source.getType()); } else if (name.equals("collect")) {//$NON-NLS-1$ // The result type for collect must be flattened C elementType = expr.getType(); if (elementType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) elementType; elementType = CollectionUtil.getFlattenedElementType(ct); } if (source.getType() instanceof SequenceType || source.getType() instanceof OrderedSetType) { astNode.setType(getSequenceType(exprCS, env, elementType)); } else { astNode.setType(getBagType(exprCS, env, elementType)); } } else if (name.equals("collectNested")) {//$NON-NLS-1$ if (source.getType() instanceof SequenceType || source.getType() instanceof OrderedSetType) { astNode.setType(getSequenceType(exprCS, env, expr.getType())); } else { astNode.setType(getBagType(exprCS, env, expr.getType())); } } else if (name.equals("any")) {//$NON-NLS-1$ @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source.getType(); astNode.setType(ct.getElementType()); } else if (name.equals("sortedBy")) {//$NON-NLS-1$ if ((source.getType() instanceof SequenceType) || source.getType() instanceof BagType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source .getType(); astNode.setType(getSequenceType(exprCS, env, ct .getElementType())); } else { // set, ordered set @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source .getType(); astNode.setType(getOrderedSetType(exprCS, env, ct .getElementType())); } } else if (name.equals("closure")) {//$NON-NLS-1$ // get the body element type if it is a collection-type // expression C bodyType = expr.getType(); if ((bodyType instanceof OrderedSetType) || (bodyType instanceof SequenceType)) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) bodyType; bodyType = ct.getElementType(); astNode.setType(getOrderedSetType(exprCS, env, bodyType)); } else { if (bodyType instanceof CollectionType) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) bodyType; bodyType = ct.getElementType(); } astNode.setType(getSetType(exprCS, env, bodyType)); } } astNode.setBody(expr); astNode.setSource(source); env.deleteElement(vdcl.getName()); if (vdcl1 != null) { env.deleteElement(vdcl1.getName()); } return astNode; } /** * Ovverridden by subclasses to assign the AST Operation target for an * iterator reference from the CST. The default implementation does nothing. * * @param simpleNameCS * the iterator name * @param env * the current OCL environment * * @since 1.3 */ protected void resolveIteratorOperation(SimpleNameCS simpleNameCS, Environment env) { // nothing to do } /** * IterateExpCS * * @param iterateExpCS * the IterateExpCS CSTNode * @param env * the OCL environment * @return the parsed IterateExp */ protected IterateExp iterateExpCS(IterateExpCS iterateExpCS, Environment env) { IterateExp astNode = null; OCLExpression source = getCollectionSourceExpression(iterateExpCS .getSource(), env); if (source == null) { return null; } Variable vdcl = null; Variable vdcl1 = null; OCLExpression expr = null; vdcl = variableDeclarationCS(iterateExpCS.getVariable1(), env, true); if (iterateExpCS.getVariable2() != null) { vdcl1 = variableDeclarationCS(iterateExpCS.getVariable2(), env, true); } /* * If there is only one variable declaration, then it is the result, * vdcl1. The missing variable declaration is treated as the implicit * iterator vdcl. */ if (vdcl1 == null) { vdcl1 = vdcl; vdcl = null; } if (vdcl == null) { // synthesize a new variable declaration. @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source.getType(); vdcl = genVariableDeclaration(iterateExpCS, "iterateExpCS", env, null, //$NON-NLS-1$ ct.getElementType(), null, false, true, false); } if (vdcl.getType() == null) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) source.getType(); vdcl.setType(ct.getElementType()); } TRACE("iterateExpCS", "iterate");//$NON-NLS-2$//$NON-NLS-1$ astNode = oclFactory.createIterateExp(); initASTMapping(env, astNode, iterateExpCS); astNode.setName("iterate"); //$NON-NLS-1$ resolveIteratorOperation(iterateExpCS.getSimpleNameCS(), env); if (isErrorNode(source)) { // don't attempt to parse iterate body for an unparseable source expr = createDummyInvalidLiteralExp(env, iterateExpCS); // don't parse call expressions sourced on this result markAsErrorNode(astNode); } else { expr = oclExpressionCS(iterateExpCS.getBody(), env); } List> iterator = astNode.getIterator(); iterator.add(vdcl); astNode.setSource(source); astNode.setResult(vdcl1); astNode.setBody(expr); if (vdcl1.getType() == null) { String message = OCLMessages.bind( OCLMessages.DeclarationType_ERROR_, vdcl1.getName()); ERROR(vdcl, "iterateExpCS", message);//$NON-NLS-1$ vdcl1.setType(createDummyInvalidType(env, iterateExpCS .getVariable1(), message)); } astNode.setType(vdcl1.getType()); if (vdcl1.getInitExpression() == null) { String message = OCLMessages.bind( OCLMessages.DeclarationNoInitExp_ERROR_, vdcl1.getName()); ERROR(iterateExpCS.getVariable2(), "iterateExpCS", message);//$NON-NLS-1$ } if (vdcl.getInitExpression() != null) { String message = OCLMessages.bind( OCLMessages.DeclarationInitExp_ERROR_, vdcl1.getName()); ERROR(iterateExpCS.getVariable1(), "iterateExpCS", message);//$NON-NLS-1$ } env.deleteElement(vdcl.getName()); env.deleteElement(vdcl1.getName()); return astNode; } /** * ModelPropertyCallExpCS * * @param modelPropertyCallExpCS * the ModelPropertyCallExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression modelPropertyCallExpCS( FeatureCallExpCS modelPropertyCallExpCS, Environment env) { OCLExpression astNode = null; if (modelPropertyCallExpCS instanceof OperationCallExpCS) { astNode = operationCallExpCS( (OperationCallExpCS) modelPropertyCallExpCS, env); } else { OCLExpression source = oclExpressionCS(modelPropertyCallExpCS .getSource(), env); if (source == null) { // PropertyCallExpCS[C] or EnumLiteralExpCS EList sequenceOfNames = createSequenceOfNames(modelPropertyCallExpCS.getPathNameCS(), null); String lastToken = modelPropertyCallExpCS.getSimpleNameCS().getValue(); EL literal = null; P attribute = null; C enumType = lookupClassifier(modelPropertyCallExpCS.getPathNameCS(), env, sequenceOfNames); if (enumType == null) { // Check to see whether the pathname corresponds to a type sequenceOfNames.add(lastToken); C type = lookupClassifier(modelPropertyCallExpCS.getSimpleNameCS(), env, sequenceOfNames); if (type == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedType_ERROR_, sequenceOfNames); ERROR(modelPropertyCallExpCS, "enumerationOrClassLiteralExpCS", message);//$NON-NLS-1$ } else { astNode = typeCS(modelPropertyCallExpCS, env, type); modelPropertyCallExpCS.getPathNameCS().setAst(uml.getPackage(type)); } } else { if (uml.isEnumeration(enumType)) { // look first for an enumeration literal with this name, rather // than a static attribute literal = uml.getEnumerationLiteral(enumType, lastToken); if ((literal == null) && isEscaped(lastToken)) { literal = uml.getEnumerationLiteral(enumType, unescape(lastToken)); } if (literal == null) { // try looking for a static attribute attribute = lookupProperty(modelPropertyCallExpCS, env, enumType, lastToken); } } else { // look for a static attribute attribute = lookupProperty(modelPropertyCallExpCS, env, enumType, lastToken); } if (literal != null) { astNode = oclFactory.createEnumLiteralExp(); initASTMapping(env, astNode, modelPropertyCallExpCS); @SuppressWarnings("unchecked") EnumLiteralExp litExp = (EnumLiteralExp) astNode; litExp.setReferredEnumLiteral(literal); astNode = litExp; astNode.setType(enumType); modelPropertyCallExpCS.getSimpleNameCS().setAst(literal); } else if (attribute != null) { if (!uml.isStatic(attribute)) { String message = OCLMessages.bind( OCLMessages.NonStaticAttribute_ERROR_, lastToken); ERROR(modelPropertyCallExpCS.getSimpleNameCS(), "enumerationOrClassLiteralExpCS", message);//$NON-NLS-1$ } PropertyCallExp pcExp = oclFactory .createPropertyCallExp(); initASTMapping(env, pcExp, modelPropertyCallExpCS); astNode = pcExp; modelPropertyCallExpCS.getSimpleNameCS().setAst(attribute); modelPropertyCallExpCS.getPathNameCS().setAst(enumType); TypeExp typeExp = typeCS(modelPropertyCallExpCS, env, enumType); initStartEndPositions(typeExp, modelPropertyCallExpCS.getPathNameCS()); pcExp.setSource(typeExp); pcExp.setReferredProperty(attribute); pcExp.setType(getPropertyType(modelPropertyCallExpCS .getSimpleNameCS(), env, enumType, attribute)); initPropertyPositions(pcExp, modelPropertyCallExpCS.getSimpleNameCS()); } else { // try looking for a nested classifier sequenceOfNames.add(lastToken); C type = lookupClassifier(modelPropertyCallExpCS.getSimpleNameCS(), env, sequenceOfNames); if (type == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedEnum_ERROR_, lastToken); ERROR(modelPropertyCallExpCS, "enumerationOrClassLiteralExpCS", message);//$NON-NLS-1$ } else { astNode = typeCS(modelPropertyCallExpCS, env, type); modelPropertyCallExpCS.getSimpleNameCS().setAst(type); modelPropertyCallExpCS.getPathNameCS().setAst( uml.getPackage(type)); } } } if (astNode == null) { astNode = createDummyInvalidLiteralExp(env, modelPropertyCallExpCS); } String traceText = new String(); for (String next : sequenceOfNames) { traceText += next + "::"; //$NON-NLS-1$ } traceText += lastToken; TRACE("enumerationOrClassLiteralExpCS", traceText); //$NON-NLS-1$ return astNode; } astNode = simpleNameCS(modelPropertyCallExpCS.getSimpleNameCS(), env, source); List> qualifiers = qualifiersCS( modelPropertyCallExpCS.getArguments(), env, astNode); if (isAtPre(modelPropertyCallExpCS)) { if (astNode instanceof FeatureCallExp) { ((FeatureCallExp) astNode).setMarkedPre(true); } else { ERROR( modelPropertyCallExpCS.getIsMarkedPreCS(), "modelPropertyCallExpCS", OCLMessages.IllegalAtPre_ERROR_);//$NON-NLS-1$ } } if (!qualifiers.isEmpty()) { if (astNode instanceof NavigationCallExp) { @SuppressWarnings("unchecked") NavigationCallExp callNode = (NavigationCallExp) astNode; setQualifiers(env, "modelPropertyCallExpCS",//$NON-NLS-1$ callNode, qualifiers); } else if ((astNode instanceof LoopExp) && (getLoopBody(astNode) instanceof NavigationCallExp)) { // might have parsed an implicit collect expression @SuppressWarnings("unchecked") NavigationCallExp nav = (NavigationCallExp) getLoopBody(astNode); setQualifiers(env, "modelPropertyCallExpCS",//$NON-NLS-1$ nav, qualifiers); } else { ERROR(modelPropertyCallExpCS, "modelPropertyCallExpCS", //$NON-NLS-1$ OCLMessages.bind(OCLMessages.IllegalQualifiers_ERROR_, computeInputString(modelPropertyCallExpCS))); } } else if (astNode instanceof AssociationClassCallExp) { @SuppressWarnings("unchecked") AssociationClassCallExp callNode = (AssociationClassCallExp) astNode; checkNotReflexive(env, "modelPropertyCallExpCS", callNode);//$NON-NLS-1$ } initASTMapping(env, astNode, modelPropertyCallExpCS); } return astNode; } @SuppressWarnings("unchecked") protected OCLExpression getLoopBody(OCLExpression expr) { return ((LoopExp) expr).getBody(); } /** * OperationCallExpCS * * @param operationCallExpCS * the OperationCallExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression operationCallExpCS( OperationCallExpCS operationCallExpCS, Environment env) { int operator = operationCallExpCS.getAccessor().getValue(); if (operator == DotOrArrowEnum.ARROW) { return arrowOperationCallExpCS(operationCallExpCS, env); } OCLExpressionCS sourceCS = operationCallExpCS.getSource(); if ((sourceCS == null) && (operationCallExpCS.getPathNameCS() != null)) { return staticOperationCallExpCS(operationCallExpCS, env); } String operationName = operationCallExpCS.getSimpleNameCS().getValue(); if (PredefinedType.OCL_IS_IN_STATE_NAME.equals(operationName)) { return oclIsInStateOperationCallExpCS(operationCallExpCS, env); } if (ParsingOptions.getValue(env, ParsingOptions.WARN_OF_XOR_OR_AND_PRECEDENCE_CHANGE)) { checkForXorOrAndPrecedenceHazard(operationCallExpCS); } OCLExpression source = oclExpressionCS(operationCallExpCS.getSource(), env); List> args = argumentsCS(operationCallExpCS, env); if (source == null) { // create an implicit source source = createImplicitSource(operationCallExpCS, env, args); } /* * If the source type is a collection and operator is ".", then there is * an implicit COLLECT operator. */ C operationSourceType = source.getType(); boolean isImplicitCollect = (operator == DotOrArrowEnum.DOT) && (operationSourceType instanceof CollectionType); if (isImplicitCollect) { @SuppressWarnings("unchecked") CollectionType ct = (CollectionType) operationSourceType; operationSourceType = ct.getElementType(); } // if the sourceType is a TypeType then this must be a static operation boolean isStatic = operationSourceType instanceof TypeType; OperationCallExp astNode = genOperationCallExp(env, operationCallExpCS, "operationCallExpCS", operationName,//$NON-NLS-1$ source, operationSourceType, args); if (isStatic) { @SuppressWarnings("unchecked") TypeType typeType = (TypeType) operationSourceType; O operation = astNode.getReferredOperation(); // operation must either be defined by the TypeType (e.g., // allInstances()) // or be a static operation of the referred classifier if (!(typeType.oclOperations().contains(operation) || uml .isStatic(operation))) { String message = OCLMessages.bind( OCLMessages.NonStaticOperation_ERROR_, operationName); ERROR(astNode, "operationCallExpCS", message);//$NON-NLS-1$ } } astNode.setMarkedPre(isAtPre(operationCallExpCS)); initPropertyPositions(astNode, operationCallExpCS.getSimpleNameCS()); OCLExpression result = astNode; if (isImplicitCollect) { result = createImplicitCollect(source, astNode, env, operationCallExpCS); } if (isErrorNode(source)) { // don't attempt to parse navigation from an unparseable source markAsErrorNode(result); } return result; } /** * @since 3.0 */ protected OCLExpression createImplicitSource(OperationCallExpCS operationCallExpCS, Environment env, List> args) { String operationName = operationCallExpCS.getSimpleNameCS().getValue(); Variable implicitSource = lookupImplicitSourceForOperation( operationCallExpCS, env, args, operationName); VariableExp vexp = createVariableExp(env, operationCallExpCS, implicitSource); if (implicitSource == null) { String errMessage = operationName + "(";//$NON-NLS-1$ for (int i = 0; i < args.size(); i++) { if (i > 0) { errMessage += ", ";//$NON-NLS-1$ } errMessage += uml.getName(args.get(i).getType()); } errMessage += ")";//$NON-NLS-1$ String message = OCLMessages.bind( OCLMessages.IllegalSignature_ERROR_, errMessage); ERROR(operationCallExpCS, "operationCallExpCS", message); //$NON-NLS-1$ } if (implicitSource != null) { vexp.setType(implicitSource.getType()); vexp.setReferredVariable(implicitSource); } else { vexp.setType(getOclVoid()); } return vexp; } /** * OperationCallExpCS for an -> * * @param operationCallExpCS * the OperationCallExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression * @since 3.0 */ protected OCLExpression arrowOperationCallExpCS( OperationCallExpCS operationCallExpCS, Environment env) { OCLExpression source = getCollectionSourceExpression(operationCallExpCS.getSource(), env); List> args = argumentsCS(operationCallExpCS, env); String operationName = operationCallExpCS.getSimpleNameCS().getValue(); C operationSourceType = source.getType(); OperationCallExp astNode = genOperationCallExp(env, operationCallExpCS, "operationCallExpCS", operationName,//$NON-NLS-1$ source, operationSourceType, args); initPropertyPositions(astNode, operationCallExpCS.getSimpleNameCS()); if (isErrorNode(source)) { // don't attempt to parse navigation from an unparseable source markAsErrorNode(astNode); } return astNode; } /** * OperationCallExpCS for oclIsInState * * @param operationCallExpCS * the OperationCallExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression * @since 3.0 */ protected OCLExpression oclIsInStateOperationCallExpCS( OperationCallExpCS operationCallExpCS, Environment env) { if (operationCallExpCS.getArguments().size() != 1) { String message = OCLMessages.bind( OCLMessages.IsInStateSignature_ERROR_, computeInputString(operationCallExpCS)); ERROR(operationCallExpCS, "operationCallExpCS", message);//$NON-NLS-1$ } OCLExpression source = oclExpressionCS(operationCallExpCS.getSource(), env); List> args = new java.util.ArrayList>(); if (!operationCallExpCS.getArguments().isEmpty()) { OCLExpressionCS arg = operationCallExpCS.getArguments().get(0); if (arg instanceof VariableExpCS) { VariableExpCS stateName = (VariableExpCS) arg; EList statePath = createSequenceOfNames(null, stateName.getSimpleNameCS()); args.add(stateExpCS(source, stateName, statePath, env)); } else if (arg instanceof FeatureCallExpCS) { FeatureCallExpCS stateName = (FeatureCallExpCS) arg; EList statePath = createSequenceOfNames(stateName.getPathNameCS(), stateName.getSimpleNameCS()); args.add(stateExpCS(source, stateName, statePath, env)); if (stateName.getSource() != null) { // FIXME ERROR } if (stateName.getIsMarkedPreCS() != null) { // FIXME ERROR } } else { String message = OCLMessages.bind( OCLMessages.IsInStateSignature_ERROR_, computeInputString(operationCallExpCS)); ERROR(arg, "operationCallExpCS", message);//$NON-NLS-1$ } } if (source == null) { // create an implicit source source = createImplicitSource(operationCallExpCS, env, args); } C operationSourceType = source.getType(); String operationName = operationCallExpCS.getSimpleNameCS().getValue(); OperationCallExp astNode = genOperationCallExp(env, operationCallExpCS, "operationCallExpCS", operationName,//$NON-NLS-1$ source, operationSourceType, args); astNode.setMarkedPre(isAtPre(operationCallExpCS)); initPropertyPositions(astNode, operationCallExpCS.getSimpleNameCS()); if (isErrorNode(source)) { // don't attempt to parse navigation from an unparseable source markAsErrorNode(astNode); } return astNode; } /** * @since 3.0 */ protected OCLExpression staticOperationCallExpCS( OperationCallExpCS operationCallExpCS, Environment env) { OCLExpression source = null; // OCLExpressionCS sourceCS = operationCallExpCS.getSource(); PathNameCS pathNameCS = operationCallExpCS.getPathNameCS(); EList pathNames = createSequenceOfNames(pathNameCS, null); C sourceType = lookupClassifier(pathNameCS, env, pathNames); if (sourceType == null) { String message = OCLMessages.bind( OCLMessages.UnrecognizedType_ERROR_, pathNames); ERROR(operationCallExpCS, "operatonCallExpCS", message);//$NON-NLS-1$ } else { source = typeCS(pathNameCS, env, sourceType); } String operationName = operationCallExpCS.getSimpleNameCS().getValue(); List> args = argumentsCS(operationCallExpCS, env); /* * If the source type is a collection and operator is ".", then there is * an implicit COLLECT operator. */ C operationSourceType = source != null ? source.getType() : null; // if the sourceType is a TypeType then this must be a static operation boolean isStatic = operationSourceType instanceof TypeType; OperationCallExp astNode = genOperationCallExp(env, operationCallExpCS, "operationCallExpCS", operationName,//$NON-NLS-1$ source, operationSourceType, args); if (isStatic) { @SuppressWarnings("unchecked") TypeType typeType = (TypeType) operationSourceType; O operation = astNode.getReferredOperation(); // operation must either be defined by the TypeType (e.g., // allInstances()) // or be a static operation of the referred classifier if (!(typeType.oclOperations().contains(operation) || uml .isStatic(operation))) { String message = OCLMessages.bind( OCLMessages.NonStaticOperation_ERROR_, operationName); ERROR(astNode, "operationCallExpCS", message);//$NON-NLS-1$ } } // astNode.setMarkedPre(isAtPre(operationCallExpCS)); initPropertyPositions(astNode, operationCallExpCS.getSimpleNameCS()); OCLExpression result = astNode; if (isErrorNode(source)) { // don't attempt to parse navigation from an unparseable source markAsErrorNode(result); } return result; } /** * @since 3.0 */ protected List> argumentsCS(OperationCallExpCS operationCallExpCS, Environment env) { List> args = new java.util.ArrayList>(); for (OCLExpressionCS arg : operationCallExpCS.getArguments()) { OCLExpression argExpr = oclExpressionCS(arg, env); if (argExpr == null) { argExpr = createDummyInvalidLiteralExp(env, arg); initASTMapping(env, argExpr, arg); } args.add(argExpr); } return args; } private void checkForXorOrAndPrecedenceHazard(OperationCallExpCS operationCallExpCS) { String parentOperationName = operationCallExpCS.getSimpleNameCS().getValue(); if ("xor".equals(parentOperationName)) { //$NON-NLS-1$ OCLExpressionCS childExpression = operationCallExpCS.getArguments().get(0); if (childExpression instanceof OperationCallExpCS) { OperationCallExpCS childOperationCallExpCS = (OperationCallExpCS)childExpression; String childOperationName = childOperationCallExpCS.getSimpleNameCS().getValue(); if (!childOperationCallExpCS.getIsAtomic() && ("or".equals(childOperationName) || "and".equals(childOperationName))) { //$NON-NLS-1$ //$NON-NLS-2$ getEnvironment().analyzerWarning(OCLMessages.XorOrAndPrecedence_WARNING, "operationCallExpCS", operationCallExpCS); //$NON-NLS-1$ } } } else if ("or".equals(parentOperationName)) { //$NON-NLS-1$ OCLExpressionCS childExpression = operationCallExpCS.getArguments().get(0); if (childExpression instanceof OperationCallExpCS) { OperationCallExpCS childOperationCallExpCS = (OperationCallExpCS)childExpression; String childOperationName = childOperationCallExpCS.getSimpleNameCS().getValue(); if (!childOperationCallExpCS.getIsAtomic() && "and".equals(childOperationName)) { //$NON-NLS-1$ getEnvironment().analyzerWarning(OCLMessages.XorOrAndPrecedence_WARNING, "operationCallExpCS", operationCallExpCS); //$NON-NLS-1$ } } } } /** * MessageExpCS * * @param messageExpCS * the MessageExpCS CSTNode * @param env * the OCL environment * @return the parsed OCLExpression */ protected OCLExpression messageExpCS(MessageExpCS messageExpCS, Environment env) { MessageExp result; OCLExpression target = oclExpressionCS(messageExpCS.getTarget(), env); String name = messageExpCS.getSimpleNameCS().getValue(); EList argsCS = messageExpCS.getArguments(); EList> arguments; if (argsCS.isEmpty()) { arguments = ECollections.emptyEList(); } else { arguments = new BasicEList>(); for (OCLMessageArgCS argCS : argsCS) { OCLExpression arg; if (argCS.getExpression() == null) { // unspecified value arg = oclFactory.createUnspecifiedValueExp(); initASTMapping(env, arg, argCS); initStartEndPositions(arg, argCS); if (argCS.getTypeCS() == null) { // OclVoid matches any parameter type in an operation // signature arg.setType(env.getOCLStandardLibrary().getOclVoid()); } else { arg.setType(typeCS(argCS.getTypeCS(), env)); initTypePositions((UnspecifiedValueExp) arg, argCS .getTypeCS()); } } else { arg = oclExpressionCS(argCS.getExpression(), env); } arguments.add(arg); } } O calledOperation = lookupOperation(messageExpCS, env, target.getType(), name, arguments); C receivedSignal = lookupSignal(messageExpCS, env, target.getType(), name, arguments); if ((calledOperation == null) && (receivedSignal == null)) { ERROR( messageExpCS, "messageExpCS", OCLMessages.bind(OCLMessages.UnrecognizedMessageType_ERROR_, name)); //$NON-NLS-1$ } else if ((calledOperation != null) && (receivedSignal != null)) { ERROR( messageExpCS, "messageExpCS", OCLMessages.bind(OCLMessages.AmbiguousMessageType_ERROR_, name)); //$NON-NLS-1$ } result = oclFactory.createMessageExp(); initASTMapping(env, result, messageExpCS); initStartEndPositions(result, messageExpCS); initPropertyPositions(result, messageExpCS.getSimpleNameCS()); result.setTarget(target); result.getArgument().addAll(arguments); EObject behavioralFeature = null; if (calledOperation != null) { COA callAction = uml.createCallOperationAction(calledOperation); initASTMapping(env, callAction, messageExpCS.getSimpleNameCS()); result.setCalledOperation(callAction); behavioralFeature = (EObject) calledOperation; } else if (receivedSignal != null) { SSA sendAction = uml.createSendSignalAction(receivedSignal); initASTMapping(env, sendAction, messageExpCS.getSimpleNameCS()); result.setSentSignal(sendAction); behavioralFeature = (EObject) receivedSignal; } if (messageExpCS.getKind() == MessageExpKind.HAS_SENT_LITERAL) { result.setType(env.getOCLStandardLibrary().getBoolean()); } else if ((behavioralFeature != null) && uml.isOperation(behavioralFeature)) { result.setType(getSequenceType(messageExpCS, env, getOperationMessageType(messageExpCS, env, calledOperation))); } else if (receivedSignal != null) { result.setType(getSequenceType(messageExpCS, env, getSignalMessageType(messageExpCS, env, receivedSignal))); } else { result.setType(getOclVoid()); } return result; } protected C getCommonSuperType(CSTNode cstNode, String rule, Environment env, C type1, C type2) { C commonType = TypeUtil.commonSuperType(cstNode, env, type1, type2); if (commonType == null) { commonType = env.getOCLStandardLibrary().getOclVoid(); } return commonType; } protected C getOCLType( Environment env, Object metaElement) { return TypeUtil.resolveType(env, uml.getOCLType(metaElement)); } protected C getSetType(CSTNode cstNode, Environment env, C elementType) { C setType = TypeUtil.resolveSetType(env, elementType); initASTMapping(env, setType, cstNode); return setType; } protected C getOrderedSetType(CSTNode cstNode, Environment env, C elementType) { C orderedSetType = TypeUtil.resolveOrderedSetType(env, elementType); initASTMapping(env, orderedSetType, cstNode); return orderedSetType; } protected C getBagType(CSTNode cstNode, Environment env, C elementType) { C bagType = TypeUtil.resolveBagType(env, elementType); initASTMapping(env, bagType, cstNode); return bagType; } protected C getSequenceType(CSTNode cstNode, Environment env, C elementType) { C sequenceType = TypeUtil.resolveSequenceType(env, elementType); initASTMapping(env, sequenceType, cstNode); return sequenceType; } /** * Obtains the current environment's representation of the collection type * of the specified kind on the given element type. As a side-effect, the * specified CST note is linked to the result, as its AST node mapping. * * @param cstNode * the concrete syntax of a collection-type reference * @param env * the current environment * @param kind * the collection kind to retrieve * @param elementType * the collection type's element type * * @return the current environment's matching collection type * * @since 1.3 */ protected C getCollectionType(CSTNode cstNode, Environment env, CollectionKind kind, C elementType) { @SuppressWarnings("deprecation") C collectionType = getCollectionType(env, kind, elementType); initASTMapping(env, collectionType, cstNode); return collectionType; } /** * Obtains the current environment's representation of the collection type * of the specified kind on the given element type. * * @param env * the current environment * @param kind * the collection kind to retrieve * @param elementType * the collection type's element type * * @return the current environment's matching collection type * * @deprecated Since 1.3, use the * {@link #getCollectionType(CSTNode, Environment, CollectionKind, Object)} * method, instead. */ @Deprecated // Use getCollectionType(cstNode, env, elementType) protected C getCollectionType( Environment env, CollectionKind kind, C elementType) { return TypeUtil.resolveCollectionType(env, kind, elementType); } protected C getTupleType(CSTNode cstNode, Environment env, EList> parts) { C tupleType = TypeUtil.resolveTupleType(env, parts); initASTMapping(env, tupleType, cstNode); return tupleType; } protected C getTypeType(CSTNode cstNode, Environment env, C type) { C typeType = TypeUtil.resolveTypeType(env, type); initASTMapping(env, typeType, cstNode); return typeType; } protected C getOperationMessageType(CSTNode cstNode, Environment env, O operation) { C operationMessageType = TypeUtil.resolveOperationMessageType(env, operation); initASTMapping(env, operationMessageType, cstNode); return operationMessageType; } protected C getSignalMessageType(CSTNode cstNode, Environment env, C signal) { C signalMessageType = TypeUtil.resolveSignalMessageType(env, signal); initASTMapping(env, signalMessageType, cstNode); return signalMessageType; } /** * Obtains the type, in the current environment, of the specified property. * As a side-effect, the CST node is configured with traceability to the * resulting type and the referenced property. * * @param cstNode * a property-call or property-context concrete syntax * @param env * the current OCL parsing environment * @param owner * the contextual classifier of the property reference * @param property * the referenced property * @return the property's type * * @since 1.3 */ protected C getPropertyType(CSTNode cstNode, Environment env, C owner, P property) { C propertyType = TypeUtil.getPropertyType(env, owner, property); initASTMapping(env, propertyType, cstNode, property); return propertyType; } @SuppressWarnings("unchecked") protected C getElementType(C possibleCollectionType) { if (possibleCollectionType instanceof CollectionType) { return ((CollectionType) possibleCollectionType) .getElementType(); } return possibleCollectionType; } @SuppressWarnings("unchecked") protected CollectionKind getCollectionKind(C possibleCollectionType) { if (possibleCollectionType instanceof CollectionType) { return ((CollectionType) possibleCollectionType).getKind(); } return null; } /** * Creates a dummy expression of invalid-literal type to be a placeholder * for a (sub)expression that could not be parsed. The resulting expression * is {@linkplain #markAsErrorNode(OCLExpression) marked} as an error * place-holder expression. * * @return the dummy invalid-literal expression * * @see #markAsErrorNode(OCLExpression) * * @deprecated Use the * {@link #createDummyInvalidLiteralExp(Environment, CSTNode)} * method, instead */ @Deprecated protected InvalidLiteralExp createDummyInvalidLiteralExp() { InvalidLiteralExp result = oclFactory.createInvalidLiteralExp(); result.setType(getStandardLibrary().getOclInvalid()); markAsErrorNode(result); return result; } /** * Creates a dummy expression of invalid-literal type to be a placeholder * for a (sub)expression that could not be parsed. The resulting expression * is {@linkplain #markAsErrorNode(OCLExpression) marked} as an error * place-holder expression. * * @param env * the contextual parsing environment * @param cstNode * the concrete-syntax node that could not be analyzed * * @return the dummy invalid-literal expression * * @see #markAsErrorNode(OCLExpression) * @see #createDummyInvalidLiteralExp() * * @since 1.3 */ protected InvalidLiteralExp createDummyInvalidLiteralExp( Environment env, CSTNode cstNode) { @SuppressWarnings("deprecation") InvalidLiteralExp result = createDummyInvalidLiteralExp(); initASTMapping(env, result, cstNode); return result; } /** * Return the type used to terminate the AST reference from cstNode that * failed to be resolved due to message. * * @param env * the current OCL parsing environment * @param cstNode * a concrete syntax node that could not be resolved * @param message * the reason for the failure to resolve. Subclasses may choose * to log this message in some way * * @return the dummy Invalid type * * @since 1.3 */ protected C createDummyInvalidType( Environment env, CSTNode cstNode, String message) { C astNode = getOclVoid(); cstNode.setAst(astNode); return astNode; } /** * Return the package used to terminate the AST reference from an implicit * PackageDeclarationCS. This default implementation simply returns * null. Subclasses may override to create a more interesting * package. * * @param env * the current OCL parsing environment * @param packageDeclarationCS * the concrete syntax of the package declaration * * @return the dumy package, or null if non is required * * @since 1.3 */ protected Object createDummyPackage( Environment env, PackageDeclarationCS packageDeclarationCS) { return null; } /** *

* Queries whether the specified expression is a placeholder that was * created for an expression that failed to parse. An example is the * expression returned by {@link #createDummyInvalidLiteralExp()}. *

*

* The default implementation simply returns false; subclasses * should override if necessary, in conjunction with the * {@link #markAsErrorNode(TypedElement)} method. *

* * @param expr * a (sub)expression * @return whether the expr is a placeholder for an unparseable * (sub)expression * * @see #markAsErrorNode(TypedElement) * * @since 1.2 */ protected boolean isErrorNode(TypedElement expr) { return false; } /** *

* Marks the specified (sub)expression as a placeholder for an expression * that could not be parsed. A subsequent invocation of the * {@link #isErrorNode(TypedElement)} method should recognize an expression * thus marked. Subsequent attempts to mark an expression that is already * marked have no effect. *

*

* The default implementation does nothing; subclasses should override if * necessary, in conjunction with the isErrorPlaceholder method. *

* * @param expr * an expression that takes the place of a (sub)expression that * could not be parsed * * @see #isErrorNode(TypedElement) * * @since 1.2 */ protected void markAsErrorNode(TypedElement expr) { // implemented by subclasses } protected Environment createPackageContext( Environment parent, List packageName) throws LookupException { @SuppressWarnings("unchecked") EnvironmentFactory.Lookup lookup = OCLUtil .getAdapter(environmentFactory, EnvironmentFactory.Lookup.class); return lookup.tryCreatePackageContext(parent, packageName); } @SuppressWarnings("unchecked") protected C lookupClassifier(CSTNode cstNode, Environment env, List className) { try { Environment.Lookup lookup = OCLUtil.getAdapter(env, Environment.Lookup.class); C classifier = lookup.tryLookupClassifier(className); if (cstNode != null) { cstNode.setAst(classifier); } return classifier; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? env.getOCLStandardLibrary().getOclVoid() : (C) e.getAmbiguousMatches().get(0); } } protected Variable lookupImplicitSourceForOperation(CSTNode cstNode, Environment env, List> args, String operationName) { return env.lookupImplicitSourceForOperation(operationName, args); } @SuppressWarnings("unchecked") protected O lookupOperation(CSTNode cstNode, Environment env, C owner, String name, List> args) { try { Environment.Lookup lookup = OCLUtil.getAdapter(env, Environment.Lookup.class); O operation = lookup.tryLookupOperation(owner, name, args); if (cstNode != null) { cstNode.setAst(operation); } return operation; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? null : (O) e.getAmbiguousMatches().get(0); } } @SuppressWarnings("unchecked") protected P lookupProperty(CSTNode cstNode, Environment env, C owner, String name) { try { Environment.Lookup lookup = OCLUtil.getAdapter(env, Environment.Lookup.class); P property = lookup.tryLookupProperty(owner, name); if (cstNode != null) { cstNode.setAst(property); } return property; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? null : (P) e.getAmbiguousMatches().get(0); } } @SuppressWarnings("unchecked") protected C lookupAssociationClassReference(CSTNode cstNode, Environment env, C owner, String name) { try { Environment.Lookup lookup = OCLUtil.getAdapter(env, Environment.Lookup.class); C associationClassReference = lookup .tryLookupAssociationClassReference(owner, name); if ((cstNode != null) && (associationClassReference != null)) { cstNode.setAst(associationClassReference); } return associationClassReference; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? null : (C) e.getAmbiguousMatches().get(0); } } @SuppressWarnings("unchecked") protected C lookupSignal(CSTNode cstNode, Environment env, C owner, String name, List> args) { try { Environment.Lookup lookup = OCLUtil.getAdapter(env, Environment.Lookup.class); C signal = lookup.tryLookupSignal(owner, name, args); if (cstNode != null) { cstNode.setAst(signal); } return signal; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? null : (C) e.getAmbiguousMatches().get(0); } } @SuppressWarnings("unchecked") protected S lookupState(CSTNode cstNode, Environment env, C sourceType, List statePath) { try { S state = env.lookupState(sourceType, statePath); if (cstNode != null) { cstNode.setAst(state); } return state; } catch (LookupException e) { ERROR(cstNode, null, e.getMessage()); return e.getAmbiguousMatches().isEmpty() ? null : (S) e.getAmbiguousMatches().get(0); } catch (SemanticException e) { ERROR(cstNode, "stateExpCS", //$NON-NLS-1$ e.getMessage()); return null; } } /** * Creates an ExpressionInOcl instance. Subclasses may override. * * @return an new expression-in-OCL */ protected ExpressionInOCL createExpressionInOCL() { return uml.createExpressionInOCL(); } /** * Creates an Constraint instance. Subclasses may override. * * @return an new constraint */ protected CT createConstraint() { return uml.createConstraint(); } /** * Initialize the symmetric mapping of an object (typically an astNode) to * its originating cstNode, so that AST-based analysis may report error * messages exploiting the CST context, or to support incremental AST/CST * update. Any pre-existing mapping is preserved. Mappings involving a null * object are ignored. * * @param env * the current OCL parsing environment * @param astNode * the abstract syntax node * @param cstNode * the concrete syntax node that generated it */ protected void initASTMapping( Environment env, Object astNode, CSTNode cstNode) { initASTMapping(env, astNode, cstNode, astNode); } /** * Initialize the asymmetric mapping of an object (typically an astNode) to * its originating cstNode, and of a cstNode to its consequent object * (typically an astNode) so that AST-based analysis may report error * messages exploiting the CST context, or to support incremental AST/CST * update. Any pre-existing mapping is preserved. Each mapping involving a * null object is ignored, so that for instance the toAstNode may be set * null to establish only the fromAstNode to cstNode mapping. * * @param env * the current OCL parsing environment * @param fromAstNode * the source of an AST-to-CST mapping * @param cstNode * the target of the AST-to-CST mapping and the source of a * CST-to-AST mapping * @param toAstNode * the target of the CST-to-AST mapping * * @since 1.3 */ protected void initASTMapping( Environment env, Object fromAstNode, CSTNode cstNode, Object toAstNode) { OCLUtil.getAdapter(env, BasicEnvironment2.class).initASTMapping( fromAstNode, cstNode, toAstNode); } /** * @since 3.0 */ public static EList createSequenceOfNames(PathNameCS pathNameCS, SimpleNameCS simpleNameCS) { EList sequenceOfNames = new BasicEList(); if (pathNameCS != null) { for (SimpleNameCS simpleName : pathNameCS.getSimpleNames()) sequenceOfNames.add(simpleName.getValue()); } if (simpleNameCS != null) { sequenceOfNames.add(simpleNameCS.getValue()); } return sequenceOfNames; } /** * Queries whether the specified name is escaped with an initial underscore * ('_') character. * * @param name * a name * @return whether it begins with the underscore escape prefix */ public static boolean isEscaped(String name) { return (name != null) && name.startsWith(OCL_ESCAPE_PREFIX); } /** * Obtains the unescaped name (assuming that it * {@linkplain #isEscaped(String) is escaped}) for another attempt to look * it up. * * @param name * an OCL-escaped name * @return the unescaped name */ public static String unescape(String name) { return name.substring(OCL_ESCAPE_LENGTH); } /** * Checks whether the names are equal, accounting for possibility of * underscore-escaped names. * * @param name * a possibly underscore-escaped name * @param elementName * name of an element in the model * * @return whether the element name is equivalent to this name */ public static boolean equalName(String name, String elementName) { boolean result = name.equals(elementName); if (!result && isEscaped(name)) { result = unescape(name).equals(elementName); } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy