blob: 7a75a120315d315b34407b2a7ef0a653ce7cc2fc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2012 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
*******************************************************************************/
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 <code>AbstractOCLAnalyzer</code> supports semantic analysis of a CST
* produced by an <code>AbstractOCLParser</code> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E>
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 <code>-&gt;</code> set conversion. In this case,
* if the single item evaluates to <code>null</code> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT> uml;
protected final EnvironmentFactory<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> environmentFactory;
public AbstractOCLAnalyzer(AbstractOCLParser parser) {
super(parser);
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return env.getOCLFactory();
}
@SuppressWarnings("unchecked")
public Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<C> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
String operName, List<? extends TypedElement<C>> args) {
StringBuilder result = new StringBuilder();
result.append(operName);
result.append('(');
for (Iterator<? extends TypedElement<C>> iter = args.iterator(); iter
.hasNext();) {
TypedElement<C> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
String rule, NavigationCallExp<C, P> nc,
List<OCLExpression<C>> qualifiers) {
if (nc instanceof PropertyCallExp<?, ?>) {
P source = ((PropertyCallExp<C, P>) nc).getReferredProperty();
List<P> 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<C> 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<C, O> ct = (CollectionType<C, O>) 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<C, P> acc = (AssociationClassCallExp<C, P>) nc;
C assocClass = acc.getReferredAssociationClass();
C sourceType = getElementType(nc.getSource().getType());
@SuppressWarnings("unchecked")
P property = ((PropertyCallExp<C, P>) 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
*/
protected void checkNotReflexive(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
String rule, AssociationClassCallExp<C, P> acc) {
C assocClass = acc.getReferredAssociationClass();
List<P> 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<C, PM> genVariableDeclaration(CSTNode cstNode,
String rule,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
String name, C type, OCLExpression<C> initExp,
boolean explicitFlag, boolean addToEnvironment, boolean isSelf) {
Variable<C, PM> 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<C, O> genOperationCallExp(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OperationCallExpCS operationCallExpCS, String rule,
String operName, OCLExpression<C> source, C ownerType,
List<OCLExpression<C>> args) {
OperationCallExp<C, O> 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<OCLExpression<C>> callargs = result.getArgument();
if (args != null) {
for (OCLExpression<C> 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;
C oclOwnerType = uml.getOCLType(ownerType);
if (TypeUtil.isStandardLibraryFeature(env, oclOwnerType, 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<CT> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<CT> constraints) {
PathNameCS pathNameCS = packageDeclarationCS.getPathNameCS();
EList<String> pathname;
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<ContextDeclCS> 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<CT> constraints) {
packageDeclarationCS(packageDeclarationCS, getOCLEnvironment(),
constraints);
}
/**
* ContextDeclCS
*
* @param contextDeclCS
* the <code>ContextDeclCS</code> <code>CSTNode</code>
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*/
protected void contextDeclCS(ContextDeclCS contextDeclCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<CT> 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 <code>OperationContextDeclCS</code> <code>CSTNode</code>
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*/
protected void operationContextDeclCS(
OperationContextDeclCS operationContextDeclCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<CT> 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 <code>OperationCS</code> <code>CSTNode</code>
* @param env
* the classifier context environment
* @return the operation context environment, or <code>null</code> if the
* operation could not be resolved
*/
protected Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> operationCS(
OperationCS operationCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
O operation = null;
C classifier = null;
EList<String> className = createSequenceOfNames(operationCS.getPathNameCS(), null);
String operationName = operationCS.getSimpleNameCS().getValue();
EList<String> qualifiedOperationName = new BasicEList<String>();
qualifiedOperationName.addAll(className);
qualifiedOperationName.add(operationName);
EList<VariableCS> 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<Variable<C, PM>> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createOperationContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OperationContextDeclCS operationContextCS, O operation) {
if (operationContextCS != null) {
operationContextCS.setAst(operation);
}
// create the operation context
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<VariableCS> 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<PM> 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<C, PM> 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 <code>VariableDeclarationCS</code>
* objects
* @param env
* the OCL expression
* @return a list of <code>VariableDeclaration</code>s
*/
protected List<Variable<C, PM>> parametersCS(List<VariableCS> parameters,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return variableDeclarationListCS(parameters, env, false);
}
/**
* PrePostOrBodyDeclCS
*
* @param prePostOrBodyDeclCS
* the <code>PrePostOrBodyDeclCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>Constraint</code>
*/
protected CT prePostOrBodyDeclCS(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
PrePostOrBodyDeclCS prePostOrBodyDeclCS) {
ExpressionInOCL<C, PM> 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<C, PM> 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<C, PM> 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<C> 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<PM> parameters = uml.getParameters(operation);
Collection<Variable<C, PM>> vars = env.getVariables();
for (Variable<C, PM> 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 <code>PropertyContextCS</code> <code>CSTNode</code>
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*
* @return the context property, or <code>null</code> if it could not be
* resolved
*/
protected P propertyContextCS(PropertyContextCS propertyContextCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<CT> constraints) {
EList<String> 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<String> propertyName = new BasicEList<String>();
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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createPropertyContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
PropertyContextCS propertyContextCS, P property) {
// create the classifier context as parent for the property context
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <code>InitOrDerValueCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>Constraint</code>
*/
protected CT initOrDerValueCS(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<C> 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<C, PM> 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 <code>ClassifierContextDeclCS</code> <code>CSTNode</code>
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*/
protected Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> classifierContextDeclCS(
ClassifierContextDeclCS classifierContextDeclCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<CT> constraints) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> result = null;
PathNameCS pathNameCS = classifierContextDeclCS.getPathNameCS();
EList<String> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createClassifierContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
ClassifierContextDeclCS classifierContextDeclCS, C classifier) {
// create the classifier context as parent for the property context
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <code>InvOrDefCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>Constraint</code>
*/
protected CT invOrDefCS(InvOrDefCS invOrDefCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <code>InvCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>Constraint</code>
*/
protected CT invCS(InvCS invCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> 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<C, PM> 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 <code>DefCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>Constraint</code>
*/
protected CT defCS(DefCS defCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> contextEnv;
DefExpressionCS defExpr = defCS.getDefExpressionCS();
EObject feature = null;
OCLExpression<C> expression = null;
CT astNode = createConstraint();
Variable<C, PM> 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<C, PM> 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<Variable<C, PM>> 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 <code>VariableDeclarationCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @param addToEnvironment
* boolean whether or not to add the the parsed variable to the
* environment
* @return the parsed <code>VariableDeclaration</code>
*/
protected Variable<C, PM> variableDeclarationCS(
VariableCS variableDeclarationCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
boolean addToEnvironment) {
String varName = variableDeclarationCS.getName();
C type = null;
if (variableDeclarationCS.getTypeCS() != null) {
type = typeCS(variableDeclarationCS.getTypeCS(), env);
}
OCLExpression<C> 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<C, O> ct = (CollectionType<C, O>) exprType;
exprType = ct.getElementType();
}
if (exprType instanceof MessageType<?, ?, ?>) {
C varType = type;
if (varType instanceof CollectionType<?, ?>) {
do {
@SuppressWarnings("unchecked")
CollectionType<C, O> collType = (CollectionType<C, O>) 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<C, PM> 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 <code>VariableDeclarationCS</code>s
* @param env
* the OCL environment
* @param addToEnvironment
* boolean whether or not to add the the parsed variable to the
* environment
* @return list of <code>VariableDeclaration</code>s
*/
protected List<Variable<C, PM>> variableDeclarationListCS(
List<VariableCS> variableDeclarationCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
boolean addToEnvironment) {
List<Variable<C, PM>> variableDeclarations = new java.util.ArrayList<Variable<C, PM>>();
for (VariableCS next : variableDeclarationCS) {
variableDeclarations.add(variableDeclarationCS(next, env,
addToEnvironment));
}
return variableDeclarations;
}
/**
* TypeCS
*
* @param typeCS
* the <code>TypeCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return an <code>EClassifier</code> representing the type
*/
protected C typeCS(TypeCS typeCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
C astNode = null;
if (typeCS instanceof PrimitiveTypeCS) {
astNode = primitiveTypeCS(((PrimitiveTypeCS) typeCS).getType(), env);
typeCS.setAst(astNode);
} else if (typeCS instanceof PathNameCS) {
EList<String> 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 <code>StateExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return a <code>StateExp</code> representing the state
* @since 3.0
*/
protected StateExp<C, S> stateExpCS(OCLExpression<C> source,
CSTNode stateExpCS, EList<String> statePath,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<C, S> 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 <code>CollectionTypeCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return an <code>EClassifier</code> representing the collection type
*/
protected C collectionTypeCS(CollectionTypeCS collectionTypeCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
CollectionKind kind = collectionTypeIdentifierCS(collectionTypeCS
.getCollectionTypeIdentifier());
C type = typeCS(collectionTypeCS.getTypeCS(), env);
C result = getCollectionType(collectionTypeCS, env, kind, type);
@SuppressWarnings("unchecked")
CollectionType<C, O> astNode = (CollectionType<C, O>) result;
initTypePositions(astNode, collectionTypeCS.getTypeCS());
return result;
}
/**
* CollectionTypeIdentifierCS
*
* @param collectionTypeIdentifier
* the <code>CollectionTypeIdentifierEnum</code> representing the
* collection type
* @return the parsed <code>CollectionType</code>
*/
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 <code>TupleTypeCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>TupleTypeCS</code>
*/
protected C tupleTypeCS(TupleTypeCS tupleTypeCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
Set<String> names = new HashSet<String>();
String nodeName = null;
EList<Variable<C, PM>> vdcls = new BasicEList<Variable<C, PM>>();
String name;
List<Variable<C, PM>> variableDeclarations = variableDeclarationListCS(
tupleTypeCS.getVariables(), env, false);
for (Variable<C, PM> 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 <code>OCLExpressionCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> oclExpressionCS(OCLExpressionCS oclExpressionCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> 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 <code>VariableExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> variableExpCS(VariableExpCS variableExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
SimpleNameCS simpleNameCS = variableExpCS.getSimpleNameCS();
OCLExpression<C> astNode = simpleNameCS(simpleNameCS, env, null);
variableExpCS.setAst(astNode);
List<OCLExpression<C>> qualifiers = qualifiersCS(variableExpCS
.getArguments(), env, astNode);
if (isAtPre(variableExpCS)) {
if (astNode instanceof FeatureCallExp<?>) {
((FeatureCallExp<C>) astNode).setMarkedPre(true);
} else {
ERROR(variableExpCS.getIsMarkedPreCS(), "variableExpCS", OCLMessages.IllegalAtPre_ERROR_);//$NON-NLS-1$
}
}
if (!qualifiers.isEmpty()) {
if (astNode instanceof NavigationCallExp<?, ?>) {
@SuppressWarnings("unchecked")
NavigationCallExp<C, P> callNode = (NavigationCallExp<C, P>) 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<C, P> callNode = (NavigationCallExp<C, P>) ((LoopExp<C, ?>) 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<C, P> callNode = (AssociationClassCallExp<C, P>) astNode;
checkNotReflexive(env, "variableExpCS", callNode);//$NON-NLS-1$
}
return astNode;
}
/**
* Queries whether the specified call expression is adorned with
* <tt>{@literal @pre}</tt>.
*
* @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
* <tt>{@literal @pre}</tt>.
*
* @param variableExp
* a variable expression
*
* @return whether the expression is marked pre
*
* @since 1.3
*/
protected boolean isAtPre(VariableExpCS variableExp) {
IsMarkedPreCS atPre = variableExp.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<C, PM> createVariableExp(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
CSTNode cst, Variable<C, PM> var) {
VariableExp<C, PM> 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 <code>OCLExpressionCS</code> arguments list
* @param env
* the OCL environment
* @param navigation
* @return the parsed <code>OCLExpression</code>s list
*/
protected List<OCLExpression<C>> qualifiersCS(
List<OCLExpressionCS> arguments,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> navigation) {
if (arguments.isEmpty()) {
return Collections.emptyList();
}
List<OCLExpression<C>> qualifiers = new java.util.ArrayList<OCLExpression<C>>();
if (navigation instanceof LoopExp<?, ?>) {
@SuppressWarnings("unchecked")
LoopExp<C, ?> loopNode = (LoopExp<C, ?>) navigation;
navigation = loopNode.getBody();
}
if (navigation instanceof AssociationClassCallExp<?, ?>) {
@SuppressWarnings("unchecked")
AssociationClassCallExp<C, P> acc = (AssociationClassCallExp<C, P>) navigation;
OCLExpression<C> 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<C, P> ref = oclFactory
.createPropertyCallExp();
initASTMapping(env, ref, arg);
ref.setReferredProperty(property);
ref.setType(getPropertyType(qualifier, env, sourceType,
property));
if (source == null) {
Variable<C, PM> implicitSource = env
.lookupImplicitSourceForProperty(simpleName);
VariableExp<C, PM> src = createVariableExp(env, arg,
implicitSource);
ref.setSource(src);
}
initStartEndPositions(ref, qualifier);
initPropertyPositions(ref, qualifier);
qualifiers.add(ref);
}
}
} else {
for (OCLExpressionCS arg : arguments) {
OCLExpression<C> qualExp = oclExpressionCS(arg, env);
if (qualExp != null) {
qualifiers.add(qualExp);
}
}
}
return qualifiers;
}
/**
* IfExpCS
*
* @param ifExpCS
* the <code>IfExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>IfExpCS</code>
*/
protected IfExp<C> ifExpCS(IfExpCS ifExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> condition = oclExpressionCS(ifExpCS.getCondition(),
env);
OCLExpression<C> thenExpression = oclExpressionCS(ifExpCS
.getThenExpression(), env);
OCLExpression<C> elseExpression = oclExpressionCS(ifExpCS
.getElseExpression(), env);
TRACE("ifExpCS", " "); //$NON-NLS-2$//$NON-NLS-1$
IfExp<C> 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 <code>LetExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>LetExpCS</code>
*
* @deprecated Since 1.3, use {@link #letExp(LetExpCS, Environment)},
* instead.
*/
@SuppressWarnings("unchecked")
@Deprecated
protected LetExp<C, PM> letExpCS(LetExpCS letExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return (LetExp<C, PM>) letExp(letExpCS, env);
}
/**
* LetExpCS
*
* @param letExpCS
* the <code>LetExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
*
* @return the parsed <code>LetExpCS</code>
*
* @since 1.3
*/
protected OCLExpression<C> letExp(LetExpCS letExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
return letExpCSRecursive(letExpCS, 0, env);
}
/**
* Constructs the LetExp
*
* @param letExpCS
* the <code>LetExpCS</code> <code>CSTNode</code>
* @param index
* the index of the VariableDeclarationCS to parse
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> letExpCSRecursive(LetExpCS letExpCS, int index,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> astNode;
if (index < letExpCS.getVariables().size()) {
VariableCS variableDeclarationCS = letExpCS.getVariables().get(
index);
Variable<C, PM> 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<C> letSubExp = letExpCSRecursive(letExpCS, ++index,
env);
LetExp<C, PM> 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 <tt>simpleNameCS</tt> token. This method is largely a
* <i>template</i>, delegating to helpers that may be separately overridden
* to resolve simple names to various kinds of expression.
*
* @param simpleNameCS
* the <code>simpleNameCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @param source
* the source of the <code>simpleNameCS</code>
* @return the parsed <code>OCLExpression</code>
*
* @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<C> simpleNameCS(SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> 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<C, O> ct = (CollectionType<C, O>) sourceElementType;
sourceElementType = ct.getElementType();
}
}
// cascaded alternatives for a simpleNameCS
OCLExpression<C> 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<C>) astNode, env, simpleNameCS);
}
return astNode;
}
/**
* Attempts to parse a <tt>simpleNameCS</tt> as an association-class call
* expression.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, or <code>null</code> if the
* source is implicit
* @param owner
* the owner of the association-class end to be navigated, or
* <code>null</code> if the source is implicit
* @param simpleName
* the simple name, as a string
* @return the parsed association-class call, or <code>null</code> if the
* simple name does not resolve to a related association class
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected AssociationClassCallExp<C, P> simpleAssociationClassName(
SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> source, C owner, String simpleName) {
AssociationClassCallExp<C, P> 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<C, PM> implicitSource = env
.lookupImplicitSourceForAssociationClass(simpleName);
VariableExp<C, PM> 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<P> ends = uml.getMemberEnds(assocClass);
List<P> 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<C, P> simpleNavigationName(
SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> source, C owner, String simpleName) {
return simplePropertyName(simpleNameCS, env, source, owner, simpleName);
}
/**
* Attempts to parse a <tt>simpleNameCS</tt> as a property call expression.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, or <code>null</code> if the
* source is implicit
* @param owner
* the owner of the property to be navigated, or
* <code>null</code> if the source is implicit
* @param simpleName
* the simple name, as a string
* @return the parsed property call, or <code>null</code> if the simple name
* does not resolve to an available property
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected PropertyCallExp<C, P> simplePropertyName(
SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> source, C owner, String simpleName) {
if (simpleName == null) {
return null;
}
PropertyCallExp<C, P> 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<C, PM> implicitSource = env
.lookupImplicitSourceForProperty(simpleName);
VariableExp<C, PM> src = createVariableExp(env, simpleNameCS,
implicitSource);
result.setSource(src);
}
initPropertyPositions(result, simpleNameCS);
}
return result;
}
/**
* Attempts to parse a <tt>simpleNameCS</tt> as a type expression. For the
* sake of completeness and generality, information about a navigation
* <code>source</code> 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 <code>classifier</code> 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 <code>null</code> if the simple
* name does not resolve to an accessible type
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected TypeExp<C> simpleTypeName(SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> 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<C> 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 <tt>simpleNameCS</tt> as a variable expression. The
* navigation <code>source</code>, 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 <code>null</code> if the simple name
* does not resolve to an accessible variable
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected VariableExp<C, PM> simpleVariableName(SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> source, String simpleName) {
VariableExp<C, PM> result = null;
if (source == null) {
Variable<C, PM> 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 <tt>simpleNameCS</tt>, 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 <code>null</code> 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<C> simpleUndefinedName(SimpleNameCS simpleNameCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
OCLExpression<C> 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 <code>collect</code> 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<C, PM> createImplicitCollect(OCLExpression<C> source,
FeatureCallExp<C> propertyCall,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
CSTNode cstNode) {
@SuppressWarnings("unchecked")
C sourceElementType = ((CollectionType<C, O>) source.getType())
.getElementType();
IteratorExp<C, PM> result = oclFactory.createIteratorExp();
initASTMapping(env, result, cstNode, null);
Variable<C, PM> itervar = genVariableDeclaration(cstNode,
"modelPropertyCallCS", env,//$NON-NLS-1$
null, sourceElementType, null, false, true, false);
List<Variable<C, PM>> iters = result.getIterator();
iters.add(itervar);
result.setBody(propertyCall);
result.setName("collect");//$NON-NLS-1$
VariableExp<C, PM> 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<C, O> ct = (CollectionType<C, O>) 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 <code>SimpleTypeEnum</code> representing the primitive
* type
* @return an <code>EClassifie</code> representing the primitive type
*/
protected C primitiveTypeCS(SimpleTypeEnum simpleType,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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 <code>PrimitiveLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>LiteralExp</code>
*/
protected LiteralExp<C> primitiveLiteralExpCS(
PrimitiveLiteralExpCS primitiveLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
LiteralExp<C> 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 <code>IntegerLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>IntegerLiteralExp</code>
*/
protected IntegerLiteralExp<C> integerLiteralExpCS(
IntegerLiteralExpCS integerLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
IntegerLiteralExp<C> astNode = oclFactory.createIntegerLiteralExp();
initASTMapping(env, astNode, integerLiteralExpCS);
astNode.setLongSymbol(integerLiteralExpCS.getLongSymbol());
astNode.setType(env.getOCLStandardLibrary().getInteger());
TRACE(
"integerLiteralExpCS", "Integer: " + integerLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* UnlimitedNaturalLiteralExpCS
*
* @param unlimitedNaturalLiteralExpCS
* the <code>UnlimitedNaturalLiteralExpCS</code>
* <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>UnlimitedNaturalLiteralExp</code>
*/
protected UnlimitedNaturalLiteralExp<C> unlimitedNaturalLiteralExpCS(
UnlimitedNaturalLiteralExpCS unlimitedNaturalLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
UnlimitedNaturalLiteralExp<C> 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 <code>RealLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>RealLiteralExp</code>
*/
protected RealLiteralExp<C> realLiteralExpCS(
RealLiteralExpCS realLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
RealLiteralExp<C> 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 <code>StringLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>StringLiteralExp</code>
*/
protected StringLiteralExp<C> stringLiteralExpCS(
StringLiteralExpCS stringLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
StringLiteralExp<C> 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 <code>BooleanLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>BooleanLiteralExp</code>
*/
protected BooleanLiteralExp<C> booleanLiteralExpCS(
BooleanLiteralExpCS booleanLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
BooleanLiteralExp<C> 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 <code>NullLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>NullLiteralExp</code>
*/
protected NullLiteralExp<C> nullLiteralExpCS(
NullLiteralExpCS nullLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
NullLiteralExp<C> 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 <code>InvalidLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>InvalidLiteralExp</code>
*/
protected InvalidLiteralExp<C> invalidLiteralExpCS(
InvalidLiteralExpCS invalidLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
InvalidLiteralExp<C> 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 <code>LiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>LiteralExp</code>
*/
protected OCLExpression<C> literalExpCS(LiteralExpCS literalExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> 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 <code>TupleLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>TupleLiteralExp</code>
*/
protected TupleLiteralExp<C, P> tupleLiteralExpCS(
TupleLiteralExpCS tupleLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
TupleLiteralExp<C, P> astNode;
HashSet<String> names = new HashSet<String>();
String nodeName = null;
String name;
astNode = oclFactory.createTupleLiteralExp();
initASTMapping(env, astNode, tupleLiteralExpCS);
EList<TupleLiteralPart<C, P>> tupleParts = astNode.getPart();
TRACE("tupleLiteralExpCS", "Tuple");//$NON-NLS-2$//$NON-NLS-1$
EList<VariableCS> tupleLiteralPartListCS = tupleLiteralExpCS
.getVariables();
EList<TupleLiteralPart<C, P>> tupleLiteralParts = tupleLiteralPartListCS(
tupleLiteralPartListCS, env);
for (TupleLiteralPart<C, P> 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<C, P> 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 <code>VariableDeclarationCS</code>es
* @param env
* the OCL environment
*
* @return list of <code>TupleLiteralPart</code>s
*/
protected EList<TupleLiteralPart<C, P>> tupleLiteralPartListCS(
List<VariableCS> variableDeclarations,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
EList<TupleLiteralPart<C, P>> result = new BasicEList<TupleLiteralPart<C, P>>();
for (Iterator<VariableCS> i = variableDeclarations.iterator(); i
.hasNext();) {
result.add(tupleLiteralPartCS(i.next(), env));
}
return result;
}
/**
* tupleLiteralPartCS
*
* @param variableDeclarationCS
* the <code>VariableDeclarationCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>VariableDeclaration</code>
*/
protected TupleLiteralPart<C, P> tupleLiteralPartCS(
VariableCS variableDeclarationCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
String varName = variableDeclarationCS.getName();
C type = null;
if (variableDeclarationCS.getTypeCS() != null) {
type = typeCS(variableDeclarationCS.getTypeCS(), env);
}
OCLExpression<C> expr = null;
if (variableDeclarationCS.getInitExpression() != null) {
expr = oclExpressionCS(variableDeclarationCS.getInitExpression(),
env);
}
TupleLiteralPart<C, P> 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 <code>EnumLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>EnumLiteralExp</code>
*/
protected TypeExp<C> typeCS(CSTNode enumLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C type) {
TypeExp<C> astNode = oclFactory.createTypeExp();
initASTMapping(env, astNode, enumLiteralExpCS, null);
astNode.setReferredType(type);
astNode.setType(getTypeType(enumLiteralExpCS, env, type));
return astNode;
}
/**
* CollectionLiteralExpCS
*
* @param collectionLiteralExpCS
* the <code>CollectionLiteralExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>CollectionLiteralExp</code>
*/
protected CollectionLiteralExp<C> collectionLiteralExpCS(
CollectionLiteralExpCS collectionLiteralExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
CollectionLiteralExp<C> astNode;
astNode = null;
CollectionKind kind = null;
CollectionLiteralPart<C> collectionLiteralPartExp = null;
List<CollectionLiteralPart<C>> 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<CollectionLiteralPartCS> collectionLiteralPartsCS = collectionLiteralExpCS
.getCollectionLiteralParts();
if (!collectionLiteralPartsCS.isEmpty()) {
Iterator<CollectionLiteralPartCS> 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 <code>CollectionLiteralPartCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>CollectionLiteralPart</code>
*/
protected CollectionLiteralPart<C> collectionLiteralPartCS(
CollectionLiteralPartCS collectionLiteralPartCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
CollectionLiteralPart<C> astNode;
astNode = null;
OCLExpression<C> expr1 = null;
OCLExpression<C> expr2 = null;
CollectionRange<C> collRange = null;
CollectionItem<C> 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 <code>PropertyCallExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> propertyCallExpCS(CallExpCS propertyCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> 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 <code>LoopExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>LoopExp</code>
*/
protected LoopExp<C, PM> loopExpCS(LoopExpCS loopExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
LoopExp<C, PM> 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 <code>OCLExpressionCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> getCollectionSourceExpression(
OCLExpressionCS oclExpressionCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> astNode = oclExpressionCS(oclExpressionCS, env);
if (astNode == null) {
astNode = createDummyInvalidLiteralExp(env, oclExpressionCS);
}
/*
* The source must be a collection type.
*/
if (!(astNode.getType() instanceof CollectionType<?, ?>)) {
CollectionLiteralExp<C> 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<CollectionLiteralPart<C>> collectionParts = astNode1.getPart();
CollectionItem<C> 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 <code>IteratorExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>IteratorExp</code>
*/
protected IteratorExp<C, PM> iteratorExpCS(IteratorExpCS iteratorExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> source = getCollectionSourceExpression(iteratorExpCS
.getSource(), env);
if (source == null) {
return null;
}
String name = iteratorExpCS.getSimpleNameCS().getValue();
Variable<C, PM> vdcl = null;
Variable<C, PM> vdcl1 = null;
IteratorExp<C, PM> astNode = oclFactory.createIteratorExp();
initASTMapping(env, astNode, iteratorExpCS);
astNode.setName(name);
resolveIteratorOperation(iteratorExpCS.getSimpleNameCS(), env);
List<Variable<C, PM>> 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<C, O> ct = (CollectionType<C, O>) 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<C, O> ct = (CollectionType<C, O>) sourceType;
vdcl1.setType(ct.getElementType());
}
}
iterators.add(vdcl1);
}
} else {
// Synthesize the iterator expression.
@SuppressWarnings("unchecked")
CollectionType<C, O> ct = (CollectionType<C, O>) 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<C> 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<C, O> ct = (CollectionType<C, O>) 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<C, O> ct = (CollectionType<C, O>) 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<C, O> ct = (CollectionType<C, O>) source
.getType();
astNode.setType(getSequenceType(exprCS, env, ct
.getElementType()));
} else { // set, ordered set
@SuppressWarnings("unchecked")
CollectionType<C, O> ct = (CollectionType<C, O>) 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<C, O> ct = (CollectionType<C, O>) bodyType;
bodyType = ct.getElementType();
astNode.setType(getOrderedSetType(exprCS, env, bodyType));
}
else {
if (bodyType instanceof CollectionType<?, ?>) {
@SuppressWarnings("unchecked")
CollectionType<C, O> ct = (CollectionType<C, O>) 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
// nothing to do
}
/**
* IterateExpCS
*
* @param iterateExpCS
* the <code>IterateExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>IterateExp</code>
*/
protected IterateExp<C, PM> iterateExpCS(IterateExpCS iterateExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
IterateExp<C, PM> astNode = null;
OCLExpression<C> source = getCollectionSourceExpression(iterateExpCS
.getSource(), env);
if (source == null) {
return null;
}
Variable<C, PM> vdcl = null;
Variable<C, PM> vdcl1 = null;
OCLExpression<C> 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<C, O> ct = (CollectionType<C, O>) source.getType();
vdcl = genVariableDeclaration(iterateExpCS,
"iterateExpCS", env, null, //$NON-NLS-1$
ct.getElementType(), null, false, true, false);
}
if (vdcl.getType() == null) {
@SuppressWarnings("unchecked")
CollectionType<C, O> ct = (CollectionType<C, O>) 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<Variable<C, PM>> 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 <code>ModelPropertyCallExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> modelPropertyCallExpCS(
FeatureCallExpCS modelPropertyCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> astNode = null;
if (modelPropertyCallExpCS instanceof OperationCallExpCS) {
astNode = operationCallExpCS(
(OperationCallExpCS) modelPropertyCallExpCS, env);
} else {
OCLExpression<C> source = oclExpressionCS(modelPropertyCallExpCS
.getSource(), env);
if (source == null) { // PropertyCallExpCS[C] or EnumLiteralExpCS
EList<String> 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<C, EL> litExp = (EnumLiteralExp<C, EL>) 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<C, P> pcExp = oclFactory
.createPropertyCallExp();
initASTMapping(env, pcExp, modelPropertyCallExpCS);
astNode = pcExp;
modelPropertyCallExpCS.getSimpleNameCS().setAst(attribute);
modelPropertyCallExpCS.getPathNameCS().setAst(enumType);
TypeExp<C> 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<OCLExpression<C>> qualifiers = qualifiersCS(
modelPropertyCallExpCS.getArguments(), env, astNode);
if (isAtPre(modelPropertyCallExpCS)) {
if (astNode instanceof FeatureCallExp<?>) {
((FeatureCallExp<C>) astNode).setMarkedPre(true);
} else {
ERROR(
modelPropertyCallExpCS.getIsMarkedPreCS(),
"modelPropertyCallExpCS", OCLMessages.IllegalAtPre_ERROR_);//$NON-NLS-1$
}
}
if (!qualifiers.isEmpty()) {
if (astNode instanceof NavigationCallExp<?, ?>) {
@SuppressWarnings("unchecked")
NavigationCallExp<C, P> callNode = (NavigationCallExp<C, P>) 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<C, P> nav = (NavigationCallExp<C, P>) 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<C, P> callNode = (AssociationClassCallExp<C, P>) astNode;
checkNotReflexive(env, "modelPropertyCallExpCS", callNode);//$NON-NLS-1$
}
initASTMapping(env, astNode, modelPropertyCallExpCS);
}
return astNode;
}
@SuppressWarnings("unchecked")
protected OCLExpression<C> getLoopBody(OCLExpression<C> expr) {
return ((LoopExp<C, ?>) expr).getBody();
}
/**
* OperationCallExpCS
*
* @param operationCallExpCS
* the <code>OperationCallExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> operationCallExpCS(
OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<C> source = oclExpressionCS(operationCallExpCS.getSource(), env);
List<OCLExpression<C>> 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<C, O> ct = (CollectionType<C, O>) operationSourceType;
operationSourceType = ct.getElementType();
}
// if the sourceType is a TypeType then this must be a static operation
boolean isStatic = operationSourceType instanceof TypeType<?, ?>;
OperationCallExp<C, O> astNode = genOperationCallExp(env, operationCallExpCS,
"operationCallExpCS", operationName,//$NON-NLS-1$
source, operationSourceType, args);
if (isStatic) {
@SuppressWarnings("unchecked")
TypeType<C, O> typeType = (TypeType<C, O>) 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<C> 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<C> createImplicitSource(OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<OCLExpression<C>> args) {
String operationName = operationCallExpCS.getSimpleNameCS().getValue();
Variable<C, PM> implicitSource = lookupImplicitSourceForOperation(
operationCallExpCS, env, args, operationName);
VariableExp<C, PM> 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 <code>OperationCallExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
* @since 3.0
*/
protected OCLExpression<C> arrowOperationCallExpCS(
OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> source = getCollectionSourceExpression(operationCallExpCS.getSource(), env);
List<OCLExpression<C>> args = argumentsCS(operationCallExpCS, env);
String operationName = operationCallExpCS.getSimpleNameCS().getValue();
C operationSourceType = source.getType();
OperationCallExp<C, O> 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 <code>OperationCallExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
* @since 3.0
*/
protected OCLExpression<C> oclIsInStateOperationCallExpCS(
OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
if (operationCallExpCS.getArguments().size() != 1) {
String message = OCLMessages.bind(
OCLMessages.IsInStateSignature_ERROR_,
computeInputString(operationCallExpCS));
ERROR(operationCallExpCS, "operationCallExpCS", message);//$NON-NLS-1$
}
OCLExpression<C> source = oclExpressionCS(operationCallExpCS.getSource(), env);
List<OCLExpression<C>> args = new java.util.ArrayList<OCLExpression<C>>();
if (!operationCallExpCS.getArguments().isEmpty()) {
OCLExpressionCS arg = operationCallExpCS.getArguments().get(0);
if (arg instanceof VariableExpCS) {
VariableExpCS stateName = (VariableExpCS) arg;
EList<String> statePath = createSequenceOfNames(null, stateName.getSimpleNameCS());
args.add(stateExpCS(source, stateName, statePath, env));
} else if (arg instanceof FeatureCallExpCS) {
FeatureCallExpCS stateName = (FeatureCallExpCS) arg;
EList<String> 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<C, O> 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<C> staticOperationCallExpCS(
OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
OCLExpression<C> source = null;
// OCLExpressionCS sourceCS = operationCallExpCS.getSource();
PathNameCS pathNameCS = operationCallExpCS.getPathNameCS();
EList<String> 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<OCLExpression<C>> 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<C, O> astNode = genOperationCallExp(env, operationCallExpCS,
"operationCallExpCS", operationName,//$NON-NLS-1$
source, operationSourceType, args);
if (isStatic) {
@SuppressWarnings("unchecked")
TypeType<C, O> typeType = (TypeType<C, O>) 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
boolean isTypeTypeOperation = (typeType != null) && typeType.oclOperations().contains(operation);
if (!(isTypeTypeOperation || 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<C> result = astNode;
if (isErrorNode(source)) {
// don't attempt to parse navigation from an unparseable source
markAsErrorNode(result);
}
return result;
}
/**
* @since 3.0
*/
protected List<OCLExpression<C>> argumentsCS(OperationCallExpCS operationCallExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
List<OCLExpression<C>> args = new java.util.ArrayList<OCLExpression<C>>();
for (OCLExpressionCS arg : operationCallExpCS.getArguments()) {
OCLExpression<C> 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 <code>MessageExpCS</code> <code>CSTNode</code>
* @param env
* the OCL environment
* @return the parsed <code>OCLExpression</code>
*/
protected OCLExpression<C> messageExpCS(MessageExpCS messageExpCS,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env) {
MessageExp<C, COA, SSA> result;
OCLExpression<C> target = oclExpressionCS(messageExpCS.getTarget(), env);
String name = messageExpCS.getSimpleNameCS().getValue();
EList<OCLMessageArgCS> argsCS = messageExpCS.getArguments();
EList<OCLExpression<C>> arguments;
if (argsCS.isEmpty()) {
arguments = ECollections.emptyEList();
} else {
arguments = new BasicEList<OCLExpression<C>>();
for (OCLMessageArgCS argCS : argsCS) {
OCLExpression<C> 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<C>) 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
Object metaElement) {
return TypeUtil.resolveType(env, uml.getOCLType(metaElement));
}
protected C getSetType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C elementType) {
C setType = TypeUtil.resolveSetType(env, elementType);
initASTMapping(env, setType, cstNode);
return setType;
}
protected C getOrderedSetType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C elementType) {
C orderedSetType = TypeUtil.resolveOrderedSetType(env, elementType);
initASTMapping(env, orderedSetType, cstNode);
return orderedSetType;
}
protected C getBagType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C elementType) {
C bagType = TypeUtil.resolveBagType(env, elementType);
initASTMapping(env, bagType, cstNode);
return bagType;
}
protected C getSequenceType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
CollectionKind kind, C elementType) {
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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
CollectionKind kind, C elementType) {
return TypeUtil.resolveCollectionType(env, kind, elementType);
}
protected C getTupleType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
EList<? extends TypedElement<C>> parts) {
C tupleType = TypeUtil.resolveTupleType(env, parts);
initASTMapping(env, tupleType, cstNode);
return tupleType;
}
protected C getTypeType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C type) {
C typeType = TypeUtil.resolveTypeType(env, type);
initASTMapping(env, typeType, cstNode);
return typeType;
}
protected C getOperationMessageType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
O operation) {
C operationMessageType = TypeUtil.resolveOperationMessageType(env,
operation);
initASTMapping(env, operationMessageType, cstNode);
return operationMessageType;
}
protected C getSignalMessageType(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<C, O>) possibleCollectionType)
.getElementType();
}
return possibleCollectionType;
}
@SuppressWarnings("unchecked")
protected CollectionKind getCollectionKind(C possibleCollectionType) {
if (possibleCollectionType instanceof CollectionType<?, ?>) {
return ((CollectionType<C, O>) 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(TypedElement) marked} as an error
* place-holder expression.
*
* @return the dummy invalid-literal expression
*
* @see #markAsErrorNode(TypedElement)
*
* @deprecated Use the
* {@link #createDummyInvalidLiteralExp(Environment, CSTNode)}
* method, instead
*/
@Deprecated
protected InvalidLiteralExp<C> createDummyInvalidLiteralExp() {
InvalidLiteralExp<C> 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(TypedElement) 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(TypedElement)
* @see #createDummyInvalidLiteralExp()
*
* @since 1.3
*/
protected InvalidLiteralExp<C> createDummyInvalidLiteralExp(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
CSTNode cstNode) {
InvalidLiteralExp<C> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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
* <code>null</code>. 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 <code>null</code> if non is required
*
* @since 1.3
*/
protected Object createDummyPackage(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
PackageDeclarationCS packageDeclarationCS) {
return null;
}
/**
* <p>
* 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()}.
* </p>
* <p>
* The default implementation simply returns <code>false</code>; subclasses
* should override if necessary, in conjunction with the
* {@link #markAsErrorNode(TypedElement)} method.
* </p>
*
* @param expr
* a (sub)expression
* @return whether the <tt>expr</tt> is a placeholder for an unparseable
* (sub)expression
*
* @see #markAsErrorNode(TypedElement)
*
* @since 1.2
*/
protected boolean isErrorNode(TypedElement<C> expr) {
return false;
}
/**
* <p>
* 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.
* </p>
* <p>
* The default implementation does nothing; subclasses should override if
* necessary, in conjunction with the <tt>isErrorPlaceholder</tt> method.
* </p>
*
* @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<C> expr) {
// implemented by subclasses
}
protected Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> createPackageContext(
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> parent,
List<String> packageName)
throws LookupException {
@SuppressWarnings("unchecked")
EnvironmentFactory.Lookup<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> lookup = OCLUtil
.getAdapter(environmentFactory, EnvironmentFactory.Lookup.class);
return lookup.tryCreatePackageContext(parent, packageName);
}
@SuppressWarnings("unchecked")
protected C lookupClassifier(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<String> className) {
try {
Environment.Lookup<PK, C, O, P> 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<C, PM> lookupImplicitSourceForOperation(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
List<OCLExpression<C>> args, String operationName) {
return env.lookupImplicitSourceForOperation(operationName, args);
}
@SuppressWarnings("unchecked")
protected O lookupOperation(CSTNode cstNode,
Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C owner, String name, List<? extends TypedElement<C>> args) {
try {
Environment.Lookup<PK, C, O, P> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C owner, String name) {
try {
Environment.Lookup<PK, C, O, P> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C owner, String name) {
try {
Environment.Lookup<PK, C, O, P> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C owner, String name, List<? extends TypedElement<C>> args) {
try {
Environment.Lookup<PK, C, O, P> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
C sourceType, List<String> 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 <tt>ExpressionInOcl</tt> instance. Subclasses may override.
*
* @return an new expression-in-OCL
*/
protected ExpressionInOCL<C, PM> createExpressionInOCL() {
return uml.createExpressionInOCL();
}
/**
* Creates an <tt>Constraint</tt> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> 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<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env,
Object fromAstNode, CSTNode cstNode, Object toAstNode) {
OCLUtil.getAdapter(env, BasicEnvironment2.class).initASTMapping(
fromAstNode, cstNode, toAstNode);
}
/**
* @since 3.0
*/
public static EList<String> createSequenceOfNames(PathNameCS pathNameCS, SimpleNameCS simpleNameCS) {
EList<String> sequenceOfNames = new BasicEList<String>();
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
* (<code>'_'</code>) 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;
}
}