org.eclipse.ocl.parser.AbstractOCLAnalyzer Maven / Gradle / Ivy
/**
*
*
* Copyright (c) 2005, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* E.D.Willink - refactored to separate from OCLAnalyzer and OCLParser
* - Bugs 184048, 237126, 245586, 213886, 242236, 259818, 259819, 297541, 298128
* Adolfo Sanchez-Barbudo Herrera - Bug 237441
* Zeligsoft - Bugs 243526, 243079, 245586 (merging and docs), 213886, 179990,
* 255599, 251349, 242236, 259740
* Nicolas Rouquette - Bug 259818 (regression)
* Borland - Bug 242880
*
*
*
* $Id: AbstractOCLAnalyzer.java,v 1.45 2011/05/01 10:56:50 auhl Exp $
*/
package org.eclipse.ocl.parser;
import static org.eclipse.ocl.Environment.SELF_VARIABLE_NAME;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.LookupException;
import org.eclipse.ocl.SemanticException;
import org.eclipse.ocl.cst.BooleanLiteralExpCS;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.cst.CSTPackage;
import org.eclipse.ocl.cst.CallExpCS;
import org.eclipse.ocl.cst.ClassifierContextDeclCS;
import org.eclipse.ocl.cst.CollectionLiteralExpCS;
import org.eclipse.ocl.cst.CollectionLiteralPartCS;
import org.eclipse.ocl.cst.CollectionRangeCS;
import org.eclipse.ocl.cst.CollectionTypeCS;
import org.eclipse.ocl.cst.CollectionTypeIdentifierEnum;
import org.eclipse.ocl.cst.ContextDeclCS;
import org.eclipse.ocl.cst.DefCS;
import org.eclipse.ocl.cst.DefExpressionCS;
import org.eclipse.ocl.cst.DerValueCS;
import org.eclipse.ocl.cst.DotOrArrowEnum;
import org.eclipse.ocl.cst.FeatureCallExpCS;
import org.eclipse.ocl.cst.IfExpCS;
import org.eclipse.ocl.cst.InitOrDerValueCS;
import org.eclipse.ocl.cst.InitValueCS;
import org.eclipse.ocl.cst.IntegerLiteralExpCS;
import org.eclipse.ocl.cst.InvCS;
import org.eclipse.ocl.cst.InvOrDefCS;
import org.eclipse.ocl.cst.InvalidLiteralExpCS;
import org.eclipse.ocl.cst.IsMarkedPreCS;
import org.eclipse.ocl.cst.IterateExpCS;
import org.eclipse.ocl.cst.IteratorExpCS;
import org.eclipse.ocl.cst.LetExpCS;
import org.eclipse.ocl.cst.LiteralExpCS;
import org.eclipse.ocl.cst.LoopExpCS;
import org.eclipse.ocl.cst.MessageExpCS;
import org.eclipse.ocl.cst.MessageExpKind;
import org.eclipse.ocl.cst.NullLiteralExpCS;
import org.eclipse.ocl.cst.OCLDocumentCS;
import org.eclipse.ocl.cst.OCLExpressionCS;
import org.eclipse.ocl.cst.OCLMessageArgCS;
import org.eclipse.ocl.cst.OperationCS;
import org.eclipse.ocl.cst.OperationCallExpCS;
import org.eclipse.ocl.cst.OperationContextDeclCS;
import org.eclipse.ocl.cst.PackageDeclarationCS;
import org.eclipse.ocl.cst.PathNameCS;
import org.eclipse.ocl.cst.PrePostOrBodyDeclCS;
import org.eclipse.ocl.cst.PrePostOrBodyEnum;
import org.eclipse.ocl.cst.PrimitiveLiteralExpCS;
import org.eclipse.ocl.cst.PrimitiveTypeCS;
import org.eclipse.ocl.cst.PropertyContextCS;
import org.eclipse.ocl.cst.RealLiteralExpCS;
import org.eclipse.ocl.cst.SimpleNameCS;
import org.eclipse.ocl.cst.SimpleTypeEnum;
import org.eclipse.ocl.cst.StringLiteralExpCS;
import org.eclipse.ocl.cst.TupleLiteralExpCS;
import org.eclipse.ocl.cst.TupleTypeCS;
import org.eclipse.ocl.cst.TypeCS;
import org.eclipse.ocl.cst.UnlimitedNaturalLiteralExpCS;
import org.eclipse.ocl.cst.VariableCS;
import org.eclipse.ocl.cst.VariableExpCS;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.expressions.CollectionLiteralExp;
import org.eclipse.ocl.expressions.CollectionLiteralPart;
import org.eclipse.ocl.expressions.CollectionRange;
import org.eclipse.ocl.expressions.EnumLiteralExp;
import org.eclipse.ocl.expressions.FeatureCallExp;
import org.eclipse.ocl.expressions.IfExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.InvalidLiteralExp;
import org.eclipse.ocl.expressions.IterateExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.LetExp;
import org.eclipse.ocl.expressions.LiteralExp;
import org.eclipse.ocl.expressions.LoopExp;
import org.eclipse.ocl.expressions.MessageExp;
import org.eclipse.ocl.expressions.NavigationCallExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StateExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralPart;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.expressions.UnspecifiedValueExp;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.lpg.AbstractAnalyzer;
import org.eclipse.ocl.lpg.BasicEnvironment2;
import org.eclipse.ocl.lpg.ProblemHandler;
import org.eclipse.ocl.options.ParsingOptions;
import org.eclipse.ocl.options.ProblemOption;
import org.eclipse.ocl.types.BagType;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.MessageType;
import org.eclipse.ocl.types.OCLStandardLibrary;
import org.eclipse.ocl.types.OrderedSetType;
import org.eclipse.ocl.types.SequenceType;
import org.eclipse.ocl.types.TypeType;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.util.CollectionUtil;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.utilities.ExpressionInOCL;
import org.eclipse.ocl.utilities.OCLFactory;
import org.eclipse.ocl.utilities.PredefinedType;
import org.eclipse.ocl.utilities.TypedElement;
import org.eclipse.ocl.utilities.UMLReflection;
/**
* The AbstractOCLAnalyzer
supports semantic analysis of a CST
* produced by an AbstractOCLParser
to create the Essential OCL
* AST. It is necessary that syntactic parsing and semantic analysis are
* performed in two steps because LPG is a bottom up parser and cannot provide
* enough contextual information to create the AST on the first pass.
*
* Derived classes should extend the abstract support for EssentialOCL to full
* support for whatever language in which EssentialOCL is embedded.
*
* @since 1.2
*/
public abstract class AbstractOCLAnalyzer
extends AbstractAnalyzer {
/**
* @since 3.1
*/
public static final String OCL_ANNOTATIONS_URI = Environment.OCL_NAMESPACE_URI+"/Annotations"; //$NON-NLS-1$
/** Prefix used by OCL to escape names that clash with keywords. */
private static final String OCL_ESCAPE_PREFIX = "_"; //$NON-NLS-1$
private static final int OCL_ESCAPE_LENGTH = OCL_ESCAPE_PREFIX.length();
/**
* When a detail with this key is in an annotation with URI
* {@link Environment#OCL_NAMESPACE_URI} on a {@link CollectionLiteralExp},
* this means that the collection literal was created by the analyzer
* implicitly for a ->
set conversion. In this case,
* if the single item evaluates to null
it must not be
* added to the resulting collection by an evaluator.
*
* @since 3.1
*/
public static final String IMPLICIT_SET_CONVERSION = "IMPLICIT_SET_CONVERSION"; //$NON-NLS-1$
/*
* Factories for creating OCL AST nodes
*/
protected OCLFactory oclFactory;
protected UMLReflection uml;
protected final EnvironmentFactory environmentFactory;
public AbstractOCLAnalyzer(AbstractOCLParser parser) {
super(parser);
Environment env = getOCLEnvironment();
this.environmentFactory = env.getFactory();
oclFactory = createOCLFactory(env);
uml = env.getUMLReflection();
}
/**
* Creates/obtains the {@link OCLFactory} that I use to create OCL AST
* elements.
*
* @param env
* my OCL environment
*
* @return an appropriate factory
*/
protected OCLFactory createOCLFactory(
Environment env) {
return env.getOCLFactory();
}
@SuppressWarnings("unchecked")
public Environment getOCLEnvironment() {
return getEnvironment().getAdapter(Environment.class);
}
/**
* @since 1.3
*/
@Override
@SuppressWarnings("deprecation")
public AbstractOCLParser getAbstractParser() {
return (AbstractOCLParser) super.getParser();
}
@Override
public AbstractOCLParser getParser() {
return getAbstractParser();
}
protected C getBoolean() {
return getStandardLibrary().getBoolean();
}
protected C getOclVoid() {
return getStandardLibrary().getOclVoid();
}
protected OCLStandardLibrary getStandardLibrary() {
return getOCLEnvironment().getOCLStandardLibrary();
}
/**
* Returns true if the token kind is an identifier or keyword, otherwise
* false.
*
* @param tokenKind
* the token kind to compare
* @return true if the token kind is an identifier or keyword, otherwise
* false
*/
static public boolean isIdentifierOrKeyword(int tokenKind) {
switch (tokenKind) {
case OCLParsersym.TK_self :
case OCLParsersym.TK_inv :
case OCLParsersym.TK_pre :
case OCLParsersym.TK_post :
case OCLParsersym.TK_body :
case OCLParsersym.TK_context :
case OCLParsersym.TK_package :
case OCLParsersym.TK_endpackage :
case OCLParsersym.TK_def :
case OCLParsersym.TK_derive :
case OCLParsersym.TK_init :
case OCLParsersym.TK_if :
case OCLParsersym.TK_then :
case OCLParsersym.TK_else :
case OCLParsersym.TK_endif :
case OCLParsersym.TK_and :
case OCLParsersym.TK_or :
case OCLParsersym.TK_xor :
case OCLParsersym.TK_not :
case OCLParsersym.TK_implies :
case OCLParsersym.TK_let :
case OCLParsersym.TK_in :
case OCLParsersym.TK_true :
case OCLParsersym.TK_false :
case OCLParsersym.TK_Set :
case OCLParsersym.TK_Bag :
case OCLParsersym.TK_Sequence :
case OCLParsersym.TK_Collection :
case OCLParsersym.TK_OrderedSet :
case OCLParsersym.TK_String :
case OCLParsersym.TK_Integer :
case OCLParsersym.TK_UnlimitedNatural :
case OCLParsersym.TK_Real :
case OCLParsersym.TK_Boolean :
case OCLParsersym.TK_Tuple :
case OCLParsersym.TK_OclAny :
case OCLParsersym.TK_OclVoid :
case OCLParsersym.TK_OclInvalid :
case OCLParsersym.TK_OclMessage :
case OCLParsersym.TK_null :
case OCLParsersym.TK_invalid :
case OCLParsersym.TK_IDENTIFIER :
case OCLParsersym.TK_EOF_TOKEN :
return true;
}
return false;
}
/**
* Constructs the string representation of an operation call.
*
* @param operName
* the operation name
* @param args
* the arguments in the operation call
*
* @return the string representation
*/
protected String operationString(
Environment env,
String operName, List extends TypedElement> args) {
StringBuffer result = new StringBuffer();
result.append(operName);
result.append('(');
for (Iterator extends TypedElement> iter = args.iterator(); iter
.hasNext();) {
TypedElement arg = iter.next();
C type = arg.getType();
result.append((type == null)
? (String) null
: uml.getName(type));
if (iter.hasNext()) {
result.append(", "); //$NON-NLS-1$
}
}
result.append(')');
return result.toString();
}
/**
* Sets the specified navigation call's qualifiers, if they are compatible
* with the navigated association end or association class.
*
* @param rule
* the rule name that parsed the qualifiers
* @param nc
* the navigation call expression
* @param qualifiers
* the qualifiers to set
*/
protected void setQualifiers(
Environment env,
String rule, NavigationCallExp nc,
List> qualifiers) {
if (nc instanceof PropertyCallExp, ?>) {
P source = ((PropertyCallExp) nc).getReferredProperty();
List expectedQualifiers = uml.getQualifiers(source);
if (expectedQualifiers.size() != qualifiers.size()) {
ERROR(qualifiers, rule, OCLMessages.bind(
OCLMessages.MismatchedQualifiers_ERROR_, nc.toString()));
return;
} else {
if (!qualifiers.isEmpty()) {
int iQualifierMax = expectedQualifiers.size();
for (int iQualifier = 0; iQualifier < iQualifierMax; iQualifier++) {
P expectedQualifier = expectedQualifiers
.get(iQualifier);
OCLExpression qualifier = qualifiers.get(iQualifier);
C expectedType = getOCLType(env, expectedQualifier);
C qualifierType = qualifier.getType();
if (!TypeUtil.compatibleTypeMatch(env, expectedType,
qualifierType)) {
ERROR(qualifier, rule, OCLMessages.bind(
OCLMessages.MismatchedQualifiers_ERROR_, nc
.toString()));
}
}
if (uml.isMany(source)) {
C ncType = nc.getType();
if (ncType instanceof CollectionType, ?>) {
// qualifying the navigation results in a
// non-collection
// type
@SuppressWarnings("unchecked")
CollectionType ct = (CollectionType) ncType;
nc.setType(ct.getElementType());
}
}
}
}
} else if (nc instanceof AssociationClassCallExp, ?>) {
if (qualifiers.size() != 1) {
ERROR(qualifiers, rule, OCLMessages.bind(
OCLMessages.AssociationClassQualifierCount_ERROR_, nc
.toString()));
}
if (qualifiers.isEmpty()) {
// already registered error
return;
}
Object qualifier = qualifiers.get(0);
if (!(qualifier instanceof PropertyCallExp, ?>)) {
ERROR(qualifier, rule, OCLMessages.bind(
OCLMessages.AssociationClassQualifierType_ERROR_, nc
.toString()));
} else {
AssociationClassCallExp acc = (AssociationClassCallExp) nc;
C assocClass = acc.getReferredAssociationClass();
C sourceType = getElementType(nc.getSource().getType());
@SuppressWarnings("unchecked")
P property = ((PropertyCallExp) qualifier)
.getReferredProperty();
// maybe look-up didn't find a property
if (property != null) {
C refAssocClass = uml.getAssociationClass(property);
if (refAssocClass == null) {
ERROR(qualifier, rule, OCLMessages.bind(
OCLMessages.AssociationClassQualifierType_ERROR_,
nc.toString()));
} else {
if (uml.getAttributes(sourceType).contains(property)
&& (refAssocClass == assocClass)) {
acc.setNavigationSource(property);
CollectionKind kind = getCollectionKind(getOCLType(
env, property));
if (kind != null) {
// FIXME associate a CSTNode with the collection
acc.setType(getCollectionType(null, env, kind,
assocClass));
} else {
acc.setType(assocClass);
}
} else {
ERROR(
qualifier,
rule,
OCLMessages
.bind(
OCLMessages.AssociationClassQualifierType_ERROR_,
nc.toString()));
}
}
}
}
}
// all's well
nc.getQualifier().addAll(qualifiers);
}
/**
* Asserts that the specified association class is not a reflexive
* association.
*
* @param env
* the current environment
* @param rule
* the rule that we are matching
* @param acc
* the association class call expression
* @param cstNode
* context of the call
*/
protected void checkNotReflexive(
Environment env,
String rule, AssociationClassCallExp acc) {
C assocClass = acc.getReferredAssociationClass();
List ends;
if (uml.isAssociationClass(assocClass)) {
ends = uml.getMemberEnds(assocClass);
} else {
ends = Collections.emptyList();
}
if (ends.size() == 2) {
P end1 = ends.get(0);
P end2 = ends.get(1);
if (TypeUtil.getPropertyType(env, assocClass, end1) == TypeUtil
.getPropertyType(env, assocClass, end2)) {
ERROR(acc, rule, OCLMessages.bind(
OCLMessages.AssociationClassAmbiguous_ERROR_, acc
.toString()));
}
}
}
/**
* Generate a VariableDeclaration AST node, and add it to the environment.
* Variable declarations are generated for "self", let expression variables,
* and iterator and iterate variables, both implicit and explicit. For
* implicit variables, the name is generated by the Environment.
*/
protected Variable genVariableDeclaration(CSTNode cstNode,
String rule,
Environment env,
String name, C type, OCLExpression initExp,
boolean explicitFlag, boolean addToEnvironment, boolean isSelf) {
Variable vdcl = oclFactory.createVariable();
initASTMapping(env, vdcl, cstNode);
vdcl.setName(name);
vdcl.setType(TypeUtil.resolveType(env, type));
vdcl.setInitExpression(initExp);
if (addToEnvironment) {
boolean result = env.addElement(name, vdcl, explicitFlag);
if (!result) {
if (name != null) {
String message = OCLMessages.bind(
OCLMessages.VariableUsed_ERROR_, name);
ERROR(cstNode, rule, message);
} else {
ERROR(cstNode, rule, OCLMessages.VariableDeclaration_ERROR_);
}
}
if (isSelf) {
env.setSelfVariable(vdcl);
}
}
if (addToEnvironment) {
TRACE(rule, "adding variable declaration for " + vdcl.getName());//$NON-NLS-1$
}
return vdcl;
}
/**
* Generate an OperationCallExp node. operName is the input name of the
* operation, which must be matched against the datatype of the operation
* source.
*
* @param env
* the current environment
* @param operationCallExpCS
* the operation call CST node
* @param rule
* the name of the concrete syntax rule that we are processing
* @param operName
* the operation name
* @param source
* the operation's source expression
* @param ownerType
* the type that defines the operation, in which we will look it
* up. This can differ from the type of the source expression in
* the case of an implicit collect iterator
* @param args
* the operation arguments
*/
protected OperationCallExp genOperationCallExp(
Environment env,
OperationCallExpCS operationCallExpCS, String rule,
String operName, OCLExpression source, C ownerType,
List> args) {
OperationCallExp result;
result = oclFactory.createOperationCallExp();
initASTMapping(env, result, operationCallExpCS);
result.setSource(source);
// Performs method signature checking
O oper = lookupOperation(operationCallExpCS.getSimpleNameCS(), env,
ownerType, operName, args);
// sometimes we use the resolved name in case the environment's look-up
// supports aliasing
String resolvedName = operName;
if (oper == null) {
String message = OCLMessages.bind(
OCLMessages.OperationNotFound_ERROR_, operationString(env,
operName, args), (ownerType == null)
? null
: uml.getName(ownerType));
ERROR(operationCallExpCS, rule, message);
result.setType(env.getOCLStandardLibrary().getOclVoid());
} else {
resolvedName = uml.getName(oper);
TRACE(rule, resolvedName);
result.setReferredOperation(oper);
}
// Set up arguments
List> callargs = result.getArgument();
if (args != null) {
for (OCLExpression arg : args) {
if (arg == null) {
ERROR(operationCallExpCS, rule, OCLMessages.BadArg_ERROR_);
} else {
callargs.add(arg);
}
}
}
// Compute the result type, and perform conformance checking.
if (oper != null) {
C resultType = null;
int opcode = 0;
if (TypeUtil.isStandardLibraryFeature(env, ownerType, oper)) {
// the operations defined intrinsically by the standard library
// are the only ones that may have opcodes
opcode = OCLStandardLibraryUtil.getOperationCode(resolvedName);
} else if (TypeUtil.isOclAnyOperation(env, oper)) {
// source is a user class, enumeration, or data type and the
// operation is defined by OclAny, not the source type
opcode = OCLStandardLibraryUtil
.getOclAnyOperationCode(resolvedName);
}
result.setOperationCode(opcode);
resultType = TypeUtil.getResultType(operationCallExpCS, env,
ownerType, oper, args);
if (resultType == null) {
resultType = getOCLType(env, oper);
}
// resolve collection or tuple type against the cache in the
// environment
resultType = TypeUtil.resolveType(env, resultType);
result.setType(resultType);
}
return result;
}
/**
* Analyzes a top-level document CS.
*
* @param documentCS
* the document
* @param constraints
* the constraints list to populate
*
* @since 1.3
*/
protected void documentCS(OCLDocumentCS documentCS, List constraints) {
for (PackageDeclarationCS decl : documentCS.getPackageDeclarations()) {
packageDeclarationCS(decl, constraints);
}
}
/**
* Analyzes a package declaration in the context of the environment created
* for an {@link OCLDocumentCS}.
*
* @param packageDeclarationCS
* the package declaration to analyze
* @param env
* the OCL document environment in which to analyze it
* @param constraints
* the constraints list to populate
*
* @since 1.3
*/
protected void packageDeclarationCS(
PackageDeclarationCS packageDeclarationCS,
Environment env,
List constraints) {
PathNameCS pathNameCS = packageDeclarationCS.getPathNameCS();
EList pathname;
Environment packageEnv;
if (pathNameCS == null) {
packageEnv = env;
pathname = ECollections.emptyEList();
initASTMapping(packageEnv, createDummyPackage(env,
packageDeclarationCS), packageDeclarationCS);
} else {
pathname = createSequenceOfNames(pathNameCS, null);
try {
packageEnv = createPackageContext(getOCLEnvironment(), pathname);
if (packageEnv != null) {
PK contextPackage = packageEnv.getContextPackage();
initASTMapping(packageEnv, contextPackage,
packageDeclarationCS);
pathNameCS.setAst(contextPackage);
}
} catch (LookupException e) {
ERROR(pathNameCS, "packageDeclarationCS", //$NON-NLS-1$
e.getMessage());
return;
}
if (packageEnv == null) {
ERROR(pathNameCS, "packageDeclarationCS", //$NON-NLS-1$
OCLMessages.bind(OCLMessages.PackageNotFound_ERROR_,
makeString(pathname)));
return;
}
}
TRACE("packageDeclarationCS", "Package ", pathname); //$NON-NLS-2$//$NON-NLS-1$
EList contextDecls = packageDeclarationCS
.getContextDecls();
for (ContextDeclCS decl : contextDecls) {
contextDeclCS(decl, packageEnv, constraints);
}
}
/**
* Parses a top-level package declaration that is not nested in an
* {@link OCLDocumentCS}.
*
* @param packageDeclarationCS
* the package declaration
* @param constraints
* the constraints list to populate
*/
protected void packageDeclarationCS(
PackageDeclarationCS packageDeclarationCS, List constraints) {
packageDeclarationCS(packageDeclarationCS, getOCLEnvironment(),
constraints);
}
/**
* ContextDeclCS
*
* @param contextDeclCS
* the ContextDeclCS
CSTNode
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*/
protected void contextDeclCS(ContextDeclCS contextDeclCS,
Environment env,
List constraints) {
if (contextDeclCS instanceof OperationContextDeclCS) {
operationContextDeclCS((OperationContextDeclCS) contextDeclCS, env,
constraints);
} else if (contextDeclCS instanceof PropertyContextCS) {
propertyContextCS((PropertyContextCS) contextDeclCS, env,
constraints);
} else if (contextDeclCS instanceof ClassifierContextDeclCS) {
classifierContextDeclCS((ClassifierContextDeclCS) contextDeclCS,
env, constraints);
}
}
/**
* OperationContextDeclCS
*
* @param operationContextDeclCS
* the OperationContextDeclCS
CSTNode
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*/
protected void operationContextDeclCS(
OperationContextDeclCS operationContextDeclCS,
Environment env,
List constraints) {
env = operationCS(operationContextDeclCS.getOperationCS(), env);
if (env != null) {
CT astNode;
for (PrePostOrBodyDeclCS decl : operationContextDeclCS
.getPrePostOrBodyDecls()) {
astNode = prePostOrBodyDeclCS(env, decl);
if (astNode != null) {
constraints.add(astNode);
}
}
}
}
/**
* OperationCS
*
* @param operationCS
* the OperationCS
CSTNode
* @param env
* the classifier context environment
* @return the operation context environment, or null
if the
* operation could not be resolved
*/
protected Environment operationCS(
OperationCS operationCS,
Environment env) {
O operation = null;
C classifier = null;
EList className = createSequenceOfNames(operationCS.getPathNameCS(), null);
String operationName = operationCS.getSimpleNameCS().getValue();
EList qualifiedOperationName = new BasicEList();
qualifiedOperationName.addAll(className);
qualifiedOperationName.add(operationName);
EList parametersCS = operationCS.getParameters();
if (className.size() > 0) {
classifier = lookupClassifier(operationCS.getPathNameCS(), env,
className);
if (classifier != null) {
// create the classifier context as parent for the operation
// context
env = environmentFactory.createClassifierContext(env,
classifier);
// ensure that the classifier context has a 'self' variable
if (env.lookupLocal(SELF_VARIABLE_NAME) == null) {
genVariableDeclaration(operationCS, "operationCS", env,//$NON-NLS-1$
SELF_VARIABLE_NAME, classifier, null, true, true, true);
}
// find the context operation
List> contextParms = parametersCS(parametersCS,
env);
operation = lookupOperation(operationCS, env, classifier,
operationName, contextParms);
operationCS.getSimpleNameCS().setAst(operation);
if (operation == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_,
makeString(qualifiedOperationName));
ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$
return null;
}
}
}
if (operation == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_,
makeString(qualifiedOperationName));
ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$
return null;
}
TypeCS operationTypeCS = operationCS.getTypeCS();
C opType1 = (operationTypeCS != null)
? typeCS(operationTypeCS, env)
: getOclVoid();
C opType2 = uml.getOCLType(operation);
if (!TypeUtil.compatibleTypeMatch(env, opType1, opType2)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceOperation_ERROR_,
makeString(qualifiedOperationName));
ERROR(operationCS, "operationContextDeclCS", message);//$NON-NLS-1$
return null;
}
TRACE("operationCS", "context", qualifiedOperationName);//$NON-NLS-2$//$NON-NLS-1$
// this ensures that parameters are correctly renamed according to the
// context declaration (thus ensuring that they do not mask attributes
// of the context classifier)
return createOperationContext(env, (OperationContextDeclCS) operationCS
.eContainer(), operation);
}
protected Environment createOperationContext(
Environment env,
OperationContextDeclCS operationContextCS, O operation) {
if (operationContextCS != null) {
operationContextCS.setAst(operation);
}
// create the operation context
Environment result = environmentFactory
.createOperationContext(env, operation);
// create a self variable for this operation
if (result.lookupLocal(SELF_VARIABLE_NAME) == null) {
genVariableDeclaration(operationContextCS,
"operationContextDeclCS", result, //$NON-NLS-1$
SELF_VARIABLE_NAME, env.getContextClassifier(), null, true,
true, true);
}
if (operationContextCS != null) {
List contextParms = operationContextCS.getOperationCS()
.getParameters();
// now, because the environment factory will have defined
// variables for operation parameters, let's replace any that
// are renamed by the context declaration
List parms = uml.getParameters(operation);
for (int i = 0; i < parms.size(); i++) {
String contextParmName = contextParms.get(i).getName();
String innateParmName = uml.getName(parms.get(i));
if (!contextParmName.equals(innateParmName)) {
Variable var = result.lookupLocal(innateParmName);
// delete this variable
result.deleteElement(innateParmName);
// replace it with this one
var.setName(contextParmName);
result.addElement(contextParmName, var, true);
}
}
}
return result;
}
/**
* ParametersCS
*
* @param parameters
* the list of parameters as VariableDeclarationCS
* objects
* @param env
* the OCL expression
* @return a list of VariableDeclaration
s
*/
protected List> parametersCS(List parameters,
Environment env) {
return variableDeclarationListCS(parameters, env, false);
}
/**
* PrePostOrBodyDeclCS
*
* @param prePostOrBodyDeclCS
* the PrePostOrBodyDeclCS
CSTNode
* @param env
* the OCL environment
* @param operation
* the context EOperation
* @return the parsed Constraint
*/
protected CT prePostOrBodyDeclCS(
Environment env,
PrePostOrBodyDeclCS prePostOrBodyDeclCS) {
ExpressionInOCL spec = createExpressionInOCL();
initASTMapping(env, spec, prePostOrBodyDeclCS, null);
O operation = env.getContextOperation();
OperationContextDeclCS operationContext = (OperationContextDeclCS) prePostOrBodyDeclCS
.eContainer();
// create a disposable child operation context for this environment
env = createOperationContext(env, operationContext, operation);
Variable selfVar = env.getSelfVariable();
spec.setContextVariable(selfVar);
C operationType = getOCLType(env, operation);
if (operationType instanceof VoidType>) {
operationType = null; // a void operation has no result
}
String stereotype = null;
Variable resultVar = null;
switch (prePostOrBodyDeclCS.getKind().getValue()) {
case PrePostOrBodyEnum.PRE :
stereotype = UMLReflection.PRECONDITION;
break;
case PrePostOrBodyEnum.POST :
stereotype = UMLReflection.POSTCONDITION;
// postconditions have an implicit variable "result" of the
// same type as the operation
if ((operationType != null)
&& (env.lookupLocal(Environment.RESULT_VARIABLE_NAME) == null)) {
resultVar = genVariableDeclaration(null,
"prePostOrBodyDeclCS0", env, //$NON-NLS-1$
Environment.RESULT_VARIABLE_NAME, operationType, null,
true, true, false);
initASTMapping(env, resultVar, prePostOrBodyDeclCS, null);
spec.setResultVariable(resultVar);
}
break;
case PrePostOrBodyEnum.BODY :
stereotype = UMLReflection.BODY;
// likewise, body conditions have an implicit variable "result"
// when using the UML-style post-condition-like form of body
// condition constraint
if ((operationType != null)
&& (env.lookupLocal(Environment.RESULT_VARIABLE_NAME) == null)) {
resultVar = genVariableDeclaration(null,
"prePostOrBodyDeclCS", env, //$NON-NLS-1$
Environment.RESULT_VARIABLE_NAME, operationType, null,
true, true, false);
initASTMapping(env, resultVar, prePostOrBodyDeclCS, null);
spec.setResultVariable(resultVar);
}
break;
}
CT astNode;
try {
OCLExpression oclExpression = oclExpressionCS(
prePostOrBodyDeclCS.getExpressionCS(), env);
/*
* create a constraint astNode -- must reference the type of self...
* also, can have a name n. type of constraint is pre/post/body...
*/
astNode = createConstraint();
initASTMapping(env, astNode, prePostOrBodyDeclCS);
SimpleNameCS simpleNameCS = prePostOrBodyDeclCS.getSimpleNameCS();
if (simpleNameCS != null) {
uml.setConstraintName(astNode, simpleNameCS.getValue());
simpleNameCS.setAst(astNode);
}
uml.addConstrainedElement(astNode, (EObject) operation);
C owner = uml.getOwningClassifier(operation);
C selfVarType = selfVar.getType();
if (owner != selfVarType) {
// implicitly redefining the operation in a specializing
// classifier
uml.addConstrainedElement(astNode, (EObject) selfVarType);
if (operationContext != null) {
// check settings for using inherited feature context in
// concrete syntax (note that the OLCHelper brings us in
// here, too, which is why we check for the context CST)
ProblemHandler.Severity sev = getEnvironment().getValue(
ProblemOption.INHERITED_FEATURE_CONTEXT);
if (!sev.isOK()) {
getEnvironment().problem(
sev,
ProblemHandler.Phase.ANALYZER,
OCLMessages.bind(
OCLMessages.NonStd_InheritedFeatureContext_,
formatQualifiedName(owner),
formatName(operation)), "prePostOrBodyDeclCS", //$NON-NLS-1$
null);
}
}
}
spec.setBodyExpression(oclExpression);
// compute the parameter variables
List parameters = uml.getParameters(operation);
Collection> vars = env.getVariables();
for (Variable var : vars) {
if (parameters.contains(var.getRepresentedParameter())) {
spec.getParameterVariable().add(var);
initASTMapping(env, var, prePostOrBodyDeclCS);
}
}
uml.setSpecification(astNode, spec);
uml.setStereotype(astNode, stereotype);
if (UMLReflection.BODY.equals(stereotype)) {
env.setBodyCondition(operation, astNode);
}
} finally {
if (resultVar != null) {
// don't want this variable to linger for the next time the
// environment
// is used, e.g. to parse a pre-condition
env.deleteElement(Environment.RESULT_VARIABLE_NAME);
}
}
return astNode;
}
/**
* PropertyContextCS
*
* @param propertyContextCS
* the PropertyContextCS
CSTNode
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*
* @return the context property, or null
if it could not be
* resolved
*/
protected P propertyContextCS(PropertyContextCS propertyContextCS,
Environment env,
List constraints) {
EList pathName = createSequenceOfNames(propertyContextCS.getPathNameCS(), null);
C owner = lookupClassifier(propertyContextCS.getPathNameCS(), env,
pathName);
if (owner == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_, makeString(pathName));
ERROR(propertyContextCS, "propertyContextCS", message);//$NON-NLS-1$
return null;
}
owner = uml.asOCLType(owner);
// create the classifier context as parent for the property context
env = environmentFactory.createClassifierContext(env, owner);
SimpleNameCS simplenameCS = propertyContextCS.getSimpleNameCS();
String simpleName = simplenameCS.getValue();
P property = lookupProperty(simplenameCS, env, owner, simpleName);
propertyContextCS.setAst(property);
EList propertyName = new BasicEList();
propertyName.addAll(pathName);
propertyName.add(simpleName);
if (property == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_,
makeString(propertyName));
ERROR(propertyContextCS, "propertyContextCS", message);//$NON-NLS-1$
return null;
}
C type = typeCS(propertyContextCS.getTypeCS(), env);
C propertyType = getPropertyType(simplenameCS, env, owner, property);
if ((type == null) || !TypeUtil.exactTypeMatch(env, propertyType, type)) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_,
makeString(propertyName));
ERROR(propertyContextCS.getTypeCS(), "propertyContextCS", message);//$NON-NLS-1$
}
TRACE("propertyContextCS", "context", propertyName); //$NON-NLS-2$//$NON-NLS-1$
// create the property context
env = environmentFactory.createAttributeContext(env, property);
InitValueCS initCS = null;
DerValueCS derCS = null;
for (InitOrDerValueCS initOrDerValueCS : propertyContextCS.getConstraints()) {
if (initOrDerValueCS instanceof InitValueCS) {
if (initCS != null) {
String message = OCLMessages.bind(
OCLMessages.PropertyConstraints_ERROR_,
makeString(propertyName));
ERROR(initOrDerValueCS, "propertyContextCS", message);//$NON-NLS-1$
}
else {
initCS = (InitValueCS) initOrDerValueCS;
CT astNode = initOrDerValueCS(env, initOrDerValueCS);
constraints.add(astNode);
}
}
else if (initOrDerValueCS instanceof DerValueCS) {
if (derCS != null) {
String message = OCLMessages.bind(
OCLMessages.PropertyConstraints_ERROR_,
makeString(propertyName));
ERROR(initOrDerValueCS, "propertyContextCS", message);//$NON-NLS-1$
}
else {
derCS = (DerValueCS) initOrDerValueCS;
CT astNode = initOrDerValueCS(env, initOrDerValueCS);
constraints.add(astNode);
}
}
}
return property;
}
protected Environment createPropertyContext(
Environment env,
PropertyContextCS propertyContextCS, P property) {
// create the classifier context as parent for the property context
Environment result = environmentFactory
.createAttributeContext(env, property);
// ensure that the classifier context has a 'self' variable
if (result.lookupLocal(SELF_VARIABLE_NAME) == null) {
genVariableDeclaration(
propertyContextCS,
"propertyContextCS", result, //$NON-NLS-1$
SELF_VARIABLE_NAME, env.getContextClassifier(), null, true,
true, true);
}
return result;
}
/**
* InitOrDerValueCS
*
* @param initOrDerValueCS
* the InitOrDerValueCS
CSTNode
* @param env
* the OCL environment
* @param property
* the context EStructuralFeature
* @return the parsed Constraint
*/
protected CT initOrDerValueCS(
Environment env,
InitOrDerValueCS initOrDerValueCS) {
P property = env.getContextProperty();
PropertyContextCS propertyContext = null;
for (EObject container = initOrDerValueCS.eContainer(); container != null; container = container
.eContainer()) {
if (container instanceof PropertyContextCS) {
propertyContext = (PropertyContextCS) container;
break;
}
}
// create a disposable property context for this environment
env = createPropertyContext(env, propertyContext, property);
String stereotype = null;
switch (initOrDerValueCS.eClass().getClassifierID()) {
case CSTPackage.INIT_VALUE_CS :
stereotype = UMLReflection.INITIAL;
break;
case CSTPackage.DER_VALUE_CS :
stereotype = UMLReflection.DERIVATION;
break;
}
OCLExpression oclExpression = oclExpressionCS(initOrDerValueCS
.getExpressionCS(), env);
CT astNode = createConstraint();
initASTMapping(env, astNode, initOrDerValueCS);
uml.addConstrainedElement(astNode, (EObject) property);
C owner = uml.getOwningClassifier(property);
if (owner != env.getSelfVariable().getType()) {
// implicitly redefining the property in a specializing classifier
uml.addConstrainedElement(astNode, (EObject) env.getSelfVariable().getType());
if (propertyContext != null) {
// check settings for using inherited feature context in
// concrete syntax (note that the OLCHelper brings us in
// here, too, which is why we check for the context CST)
ProblemHandler.Severity sev = getEnvironment().getValue(
ProblemOption.INHERITED_FEATURE_CONTEXT);
if (!sev.isOK()) {
getEnvironment().problem(
sev,
ProblemHandler.Phase.ANALYZER,
OCLMessages.bind(
OCLMessages.NonStd_InheritedFeatureContext_,
formatQualifiedName(owner), formatName(property)),
"initOrDerValueCS", //$NON-NLS-1$
null);
}
}
}
ExpressionInOCL spec = createExpressionInOCL();
initASTMapping(env, spec, initOrDerValueCS, null);
spec.setBodyExpression(oclExpression);
spec.setContextVariable(env.getSelfVariable());
uml.setSpecification(astNode, spec);
uml.setStereotype(astNode, stereotype);
if (UMLReflection.DERIVATION.equals(stereotype)) {
env.setDeriveConstraint(property, astNode);
} else {
env.setInitConstraint(property, astNode);
}
return astNode;
}
/**
* ClassifierContextDeclCS
*
* @param classifierContextDeclCS
* the ClassifierContextDeclCS
CSTNode
* @param env
* the package environment
* @param constraints
* the constraints list to populate
*
* @param the
* classifier context environment, or null
of the
* classifier could not be resolved
*/
protected Environment classifierContextDeclCS(
ClassifierContextDeclCS classifierContextDeclCS,
Environment env,
List constraints) {
Environment result = null;
PathNameCS pathNameCS = classifierContextDeclCS.getPathNameCS();
EList pathName = createSequenceOfNames(pathNameCS, null);
C type = lookupClassifier(pathNameCS, env, pathName);
if (type == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedContext_ERROR_, makeString(pathName));
ERROR(classifierContextDeclCS, "classifierContextDeclCS", message);//$NON-NLS-1$
// return null;
type = createDummyInvalidType(env, pathNameCS, pathNameCS
.toString());
}
type = uml.asOCLType(type);
result = environmentFactory.createClassifierContext(env, type);
classifierContextDeclCS.setAst(type);
if (result.getSelfVariable() == null) {
// ensure that the classifier context has a "self" variable
genVariableDeclaration(classifierContextDeclCS,
"classifierContextDeclCS", result, //$NON-NLS-1$
SELF_VARIABLE_NAME, type, null, true, true, true);
}
TRACE("classifierContextDeclCS", "context", pathName); //$NON-NLS-2$//$NON-NLS-1$
for (InvOrDefCS decl : classifierContextDeclCS.getConstraints()) {
CT constraint = invOrDefCS(decl, result);
if (constraint != null) {
constraints.add(constraint);
}
}
return result;
}
protected Environment createClassifierContext(
Environment env,
ClassifierContextDeclCS classifierContextDeclCS, C classifier) {
// create the classifier context as parent for the property context
Environment result = environmentFactory
.createClassifierContext(env, classifier);
// ensure that the classifier context has a 'self' variable
if (result.lookupLocal(SELF_VARIABLE_NAME) == null) {
genVariableDeclaration(classifierContextDeclCS,
"propertyContextCS", result, //$NON-NLS-1$
SELF_VARIABLE_NAME, env.getContextClassifier(), null, true,
true, true);
}
return result;
}
/**
* InvOrDefCS
*
* @param invOrDefCS
* the InvOrDefCS
CSTNode
* @param env
* the OCL environment
* @return the parsed Constraint
*/
protected CT invOrDefCS(InvOrDefCS invOrDefCS,
Environment env) {
C classifier = env.getContextClassifier();
// create a disposable classifier context for this environment
env = createClassifierContext(env, (ClassifierContextDeclCS) invOrDefCS
.eContainer(), classifier);
CT astNode = null;
if (invOrDefCS instanceof InvCS) {
astNode = invCS((InvCS) invOrDefCS, env);
} else if (invOrDefCS instanceof DefCS) {
astNode = defCS((DefCS) invOrDefCS, env);
}
return astNode;
}
/**
* InvCS
*
* @param invCS
* the InvCS
CSTNode
* @param env
* the OCL environment
* @return the parsed Constraint
*/
protected CT invCS(InvCS invCS,
Environment env) {
OCLExpression oclExpression = oclExpressionCS(invCS
.getExpressionCS(), env);
CT astNode = createConstraint();
SimpleNameCS simpleNameCS = invCS.getSimpleNameCS();
if (simpleNameCS != null) {
uml.setConstraintName(astNode, simpleNameCS.getValue());
}
C type = env.getContextClassifier();
uml.addConstrainedElement(astNode, (EObject) type);
ExpressionInOCL spec = createExpressionInOCL();
initASTMapping(env, astNode, invCS);
initASTMapping(env, env.getSelfVariable(), invCS, null);
initASTMapping(env, spec, invCS.getExpressionCS(), null);
spec.setBodyExpression(oclExpression);
spec.setContextVariable(env.getSelfVariable());
if (simpleNameCS != null) {
simpleNameCS.setAst(spec.getContextVariable());
}
uml.setSpecification(astNode, spec);
uml.setStereotype(astNode, UMLReflection.INVARIANT);
return astNode;
}
/**
* DefCS
*
* @param defCS
* the DefCS
CSTNode
* @param env
* the OCL environment
* @return the parsed Constraint
*/
protected CT defCS(DefCS defCS,
Environment env) {
Environment contextEnv;
DefExpressionCS defExpr = defCS.getDefExpressionCS();
EObject feature = null;
OCLExpression expression = null;
CT astNode = createConstraint();
Variable variable = null;
C operType = null;
C contextClassifier = env.getContextClassifier();
SimpleNameCS simpleNameCS = defCS.getSimpleNameCS();
if (simpleNameCS != null) {
uml.setConstraintName(astNode, simpleNameCS.getValue());
}
uml.addConstrainedElement(astNode, (EObject) contextClassifier);
ExpressionInOCL spec = createExpressionInOCL();
initASTMapping(env, astNode, defCS);
initASTMapping(env, env.getSelfVariable(), defCS, null);
initASTMapping(env, spec, defExpr);
if (simpleNameCS != null) {
simpleNameCS.setAst(spec.getContextVariable());
}
uml.setSpecification(astNode, spec);
uml.setStereotype(astNode, UMLReflection.DEFINITION);
if (defExpr != null) {
try {
if (defExpr.getVariableCS() != null) {
// context of the expression is the classifier
contextEnv = env;
variable = variableDeclarationCS(defExpr.getVariableCS(),
contextEnv, false);
spec.setResultVariable(variable);
P existing = lookupProperty(null, env, contextClassifier,
variable.getName());
if (existing != null) {
ERROR(defCS, "defCS", //$NON-NLS-1$
OCLMessages.bind(
OCLMessages.DuplicateProperty_ERROR_, variable
.getName(), uml.getName(contextEnv
.getContextClassifier())));
}
spec.setContextVariable(env.getSelfVariable());
// define the property now, so that recursive references to
// it will resolve correctly
feature = (EObject) env.defineAttribute(contextClassifier,
variable, astNode);
if (getEnvironment().getValue(
ParsingOptions.DEFINITION_CONSTRAINS_FEATURE)) {
uml.addConstrainedElement(astNode, feature);
}
expression = oclExpressionCS(defExpr.getExpressionCS(),
contextEnv);
} else if (defExpr.getOperationCS() != null) {
// context of the expression is the new operation
OperationCS operCS = defExpr.getOperationCS();
contextEnv = environmentFactory.createEnvironment(env);
List> params = variableDeclarationListCS(
operCS.getParameters(), contextEnv, true);
operType = typeCS(operCS.getTypeCS(), contextEnv);
String operName = operCS.getSimpleNameCS().getValue();
O existing = lookupOperation(null, env, contextClassifier,
operName, params);
if (existing != null) {
ERROR(defCS, "defCS", //$NON-NLS-1$
OCLMessages
.bind(OCLMessages.DuplicateOperation_ERROR_,
operationString(env, operName, params), uml
.getName(contextEnv
.getContextClassifier())));
}
spec.setContextVariable(env.getSelfVariable());
spec.getParameterVariable().addAll(params);
// define the operation now, so that recursive references to
// it will resolve correctly
feature = (EObject) env.defineOperation(contextClassifier,
operName, operType, params, astNode);
operCS.setAst(feature);
operCS.getSimpleNameCS().setAst(feature);
if (operCS.getPathNameCS() != null)
operCS.getPathNameCS().setAst(contextClassifier);
if (getEnvironment().getValue(
ParsingOptions.DEFINITION_CONSTRAINS_FEATURE)) {
uml.addConstrainedElement(astNode, feature);
}
expression = oclExpressionCS(defExpr.getExpressionCS(),
contextEnv);
}
if ((feature != null) && defCS.isStatic()) {
Boolean supportStatic = getEnvironment().getValue(
ParsingOptions.SUPPORT_STATIC_FEATURES);
if (!supportStatic) {
ERROR(defCS, "defCS", //$NON-NLS-1$
OCLMessages.bind(
OCLMessages.UnsupportedStatic_ERROR_, null));
}
else if (!uml.setIsStatic(feature, true)) {
ERROR(defCS, "defCS", //$NON-NLS-1$
OCLMessages.bind(
OCLMessages.UnimplementedStatic_ERROR_, null));
}
}
if ((feature != null) && (expression != null)) {
C featureType = getOCLType(env, feature);
C bodyType = expression.getType();
if ((featureType == null)
|| !TypeUtil.compatibleTypeMatch(env, bodyType,
featureType)) {
expression = null; // trigger undefinition of the
// feature
astNode = null; // fail to return an AST node
String message = OCLMessages.bind(
OCLMessages.DefinitionConstraintConformance_ERROR_,
uml.getName(bodyType), uml.getName(featureType));
ERROR(defCS, "defCS", message);//$NON-NLS-1$
}
spec.setBodyExpression(expression);
}
} finally {
if ((feature != null) && (expression == null)) {
// failed to parse the body expression? Undefine the feature
env.undefine(feature);
}
}
}
return astNode;
}
/**
* VariableDeclarationCS
*
* @param variableDeclarationCS
* the VariableDeclarationCS
CSTNode
* @param env
* the OCL environment
* @param addToEnvironment
* boolean whether or not to add the the parsed variable to the
* environment
* @return the parsed VariableDeclaration
*/
protected Variable variableDeclarationCS(
VariableCS variableDeclarationCS,
Environment env,
boolean addToEnvironment) {
String varName = variableDeclarationCS.getName();
C type = null;
if (variableDeclarationCS.getTypeCS() != null) {
type = typeCS(variableDeclarationCS.getTypeCS(), env);
}
OCLExpression expr = null;
if (variableDeclarationCS.getInitExpression() != null) {
expr = oclExpressionCS(variableDeclarationCS.getInitExpression(),
env);
}
// handle the generic typing of OclMessages
if (expr != null) {
C exprType = expr.getType();
while (exprType instanceof CollectionType, ?>) {
@SuppressWarnings("unchecked")
CollectionType ct = (CollectionType) exprType;
exprType = ct.getElementType();
}
if (exprType instanceof MessageType, ?, ?>) {
C varType = type;
if (varType instanceof CollectionType, ?>) {
do {
@SuppressWarnings("unchecked")
CollectionType collType = (CollectionType) varType;
varType = collType.getElementType();
if (varType == env.getOCLStandardLibrary()
.getOclMessage()) {
// substitute the actual type for the generic type
collType.setElementType(exprType);
break;
}
} while (varType instanceof CollectionType, ?>);
} else if (type == env.getOCLStandardLibrary().getOclMessage()) {
// substitute the actual type for the generic type
type = exprType;
}
}
}
Variable astNode = genVariableDeclaration(variableDeclarationCS,
"variableDeclarationCS", env, varName, type, expr, //$NON-NLS-1$
true, addToEnvironment, false);
initStartEndPositions(astNode, variableDeclarationCS);
if (variableDeclarationCS.getTypeCS() != null) {
initTypePositions(astNode, variableDeclarationCS.getTypeCS());
}
return astNode;
}
/**
* VariableDeclarationListCS
*
* @param variableDeclarationCS
* list of VariableDeclarationCS
s
* @param env
* the OCL environment
* @param addToEnvironment
* boolean whether or not to add the the parsed variable to the
* environment
* @return list of VariableDeclaration
s
*/
protected List> variableDeclarationListCS(
List variableDeclarationCS,
Environment env,
boolean addToEnvironment) {
List> variableDeclarations = new java.util.ArrayList>();
for (VariableCS next : variableDeclarationCS) {
variableDeclarations.add(variableDeclarationCS(next, env,
addToEnvironment));
}
return variableDeclarations;
}
/**
* TypeCS
*
* @param typeCS
* the TypeCS
CSTNode
* @param env
* the OCL environment
* @return an EClassifier
representing the type
*/
protected C typeCS(TypeCS typeCS,
Environment env) {
C astNode = null;
if (typeCS instanceof PrimitiveTypeCS) {
astNode = primitiveTypeCS(((PrimitiveTypeCS) typeCS).getType(), env);
typeCS.setAst(astNode);
} else if (typeCS instanceof PathNameCS) {
EList pathName = createSequenceOfNames((PathNameCS) typeCS, null);
astNode = lookupClassifier(typeCS, env, pathName);
if (astNode == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedType_ERROR_,
formatPath(pathName));
ERROR(typeCS, "typeCS", message);//$NON-NLS-1$
astNode = createDummyInvalidType(env, typeCS, message);
}
typeCS.setAst(astNode);
} else if (typeCS instanceof CollectionTypeCS
|| typeCS instanceof TupleTypeCS) {
if (typeCS instanceof CollectionTypeCS) {
astNode = collectionTypeCS((CollectionTypeCS) typeCS, env);
} else if (typeCS instanceof TupleTypeCS) {
astNode = tupleTypeCS((TupleTypeCS) typeCS, env);
}
}
return astNode;
}
/**
* stateExpCS
*
* @param source
* the source expression of the oclIsInState operation
* @param stateExpCS
* the StateExpCS
CSTNode
* @param env
* the OCL environment
* @return a StateExp
representing the state
* @since 3.0
*/
protected StateExp stateExpCS(OCLExpression source,
CSTNode stateExpCS, EList statePath,
Environment env) {
C sourceType = null;
if (source != null) {
sourceType = source.getType();
}
S state = null;
if (!statePath.isEmpty()) {
// to support content-assist, we can parse an expression that
// has no state, to provide suggestions for the first part
// of the name. Validation of the expression will assert
// the presence of some referred state
state = lookupState(stateExpCS, env, sourceType, statePath);
if (state == null) {
ERROR(stateExpCS, "stateExpCS", //$NON-NLS-1$
OCLMessages.bind(OCLMessages.NoSuchState_ERROR_, statePath,
sourceType == null
? null
: uml.getName(sourceType)));
// FIXME Set state to some unresolvedState to avoid NPEs etc
}
}
StateExp astNode = oclFactory.createStateExp();
initASTMapping(env, astNode, stateExpCS);
astNode.setReferredState(state);
astNode.setType(env.getOCLStandardLibrary().getState());
astNode.setName(makeName(statePath));
initStartEndPositions(astNode, stateExpCS);
return astNode;
}
/**
* CollectionTypeCS
*
* @param collectionTypeCS
* the CollectionTypeCS
CSTNode
* @param env
* the OCL environment
* @return an EClassifier
representing the collection type
*/
protected C collectionTypeCS(CollectionTypeCS collectionTypeCS,
Environment env) {
CollectionKind kind = collectionTypeIdentifierCS(collectionTypeCS
.getCollectionTypeIdentifier());
C type = typeCS(collectionTypeCS.getTypeCS(), env);
C result = getCollectionType(collectionTypeCS, env, kind, type);
@SuppressWarnings("unchecked")
CollectionType astNode = (CollectionType) result;
initTypePositions(astNode, collectionTypeCS.getTypeCS());
return result;
}
/**
* CollectionTypeIdentifierCS
*
* @param collectionTypeIdentifier
* the CollectionTypeIdentifierEnum
representing the
* collection type
* @return the parsed CollectionType
*/
protected CollectionKind collectionTypeIdentifierCS(
CollectionTypeIdentifierEnum collectionTypeIdentifier) {
CollectionKind astNode = null;
switch (collectionTypeIdentifier.getValue()) {
case CollectionTypeIdentifierEnum.SET :
astNode = CollectionKind.SET_LITERAL;
TRACE("collectionTypeIdentifierCS", "SET");//$NON-NLS-2$//$NON-NLS-1$
break;
case CollectionTypeIdentifierEnum.BAG :
astNode = CollectionKind.BAG_LITERAL;
TRACE("collectionTypeIdentifierCS", "BAG"); //$NON-NLS-2$//$NON-NLS-1$
break;
case CollectionTypeIdentifierEnum.SEQUENCE :
astNode = CollectionKind.SEQUENCE_LITERAL;
TRACE("collectionTypeIdentifierCS", "SEQUENCE"); //$NON-NLS-2$//$NON-NLS-1$
break;
case CollectionTypeIdentifierEnum.COLLECTION :
astNode = CollectionKind.COLLECTION_LITERAL;
TRACE("collectionTypeIdentifierCS", "COLLECTION"); //$NON-NLS-2$//$NON-NLS-1$
break;
case CollectionTypeIdentifierEnum.ORDERED_SET :
astNode = CollectionKind.ORDERED_SET_LITERAL;
TRACE("collectionTypeIdentifierCS", "ORDERED_SET"); //$NON-NLS-2$//$NON-NLS-1$
break;
}
return astNode;
}
/**
* TupleTypeCS
*
* @param tupleTypeCS
* the TupleTypeCS
CSTNode
* @param env
* the OCL environment
* @return the parsed TupleTypeCS
*/
protected C tupleTypeCS(TupleTypeCS tupleTypeCS,
Environment env) {
Set names = new HashSet();
String nodeName = null;
EList> vdcls = new BasicEList>();
String name;
List> variableDeclarations = variableDeclarationListCS(
tupleTypeCS.getVariables(), env, false);
for (Variable vdcl : variableDeclarations) {
vdcls.add(vdcl);
name = vdcl.getName();
TRACE("tupleTypeCS", " name = " + name);//$NON-NLS-2$//$NON-NLS-1$
if (names.contains(name)) {
String message = OCLMessages.bind(
OCLMessages.DuplicateNameInTuple_ERROR_, name);
ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$
vdcl.setName(null);
} else {
names.add(name);
}
if (vdcl.getInitExpression() != null) {
String message = OCLMessages.bind(
OCLMessages.InitExpNotAllowed_ERROR_, name);
ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$
}
if (vdcl.getType() == null) {
String message = OCLMessages.bind(
OCLMessages.DeclarationType_ERROR_, name);
ERROR(vdcl, "tupleTypeCS", message);//$NON-NLS-1$
vdcl.setType(getOclVoid());
}
if (nodeName == null) {
nodeName = "Tuple("; //$NON-NLS-1$
} else {
nodeName += ", "; //$NON-NLS-1$
}
nodeName += vdcl.getName() + ":" + uml.getName(vdcl.getType()); //$NON-NLS-1$
}
return getTupleType(tupleTypeCS, env, vdcls);
}
/**
* OCLExpressionCS
*
* @param oclExpressionCS
* the OCLExpressionCS
CSTNode
* @param env
* the OCL environment
* @return the parsed OCLExpression
*/
protected OCLExpression oclExpressionCS(OCLExpressionCS oclExpressionCS,
Environment env) {
OCLExpression astNode = null;
if (oclExpressionCS instanceof IfExpCS) {
astNode = ifExpCS((IfExpCS) oclExpressionCS, env);
} else if (oclExpressionCS instanceof CallExpCS) {
astNode = propertyCallExpCS((CallExpCS) oclExpressionCS, env);
} else if (oclExpressionCS instanceof VariableExpCS) {
astNode = variableExpCS((VariableExpCS) oclExpressionCS, env);
} else if (oclExpressionCS instanceof LiteralExpCS) {
astNode = literalExpCS((LiteralExpCS) oclExpressionCS, env);
} else if (oclExpressionCS instanceof LetExpCS) {
astNode = letExp((LetExpCS) oclExpressionCS, env);
} else if (oclExpressionCS instanceof MessageExpCS) {
astNode = messageExpCS((MessageExpCS) oclExpressionCS, env);
}
if (astNode != null) {
astNode.setType(TypeUtil.resolveType(env, astNode.getType()));
initStartEndPositions(astNode, oclExpressionCS);
}
return astNode;
}
/**
* VariableExpCS
*
* @param variableExpCS
* the VariableExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed OCLExpression
*/
protected OCLExpression variableExpCS(VariableExpCS variableExpCS,
Environment env) {
SimpleNameCS simpleNameCS = variableExpCS.getSimpleNameCS();
OCLExpression astNode = simpleNameCS(simpleNameCS, env, null);
variableExpCS.setAst(astNode);
List> qualifiers = qualifiersCS(variableExpCS
.getArguments(), env, astNode);
if (isAtPre(variableExpCS)) {
if (astNode instanceof FeatureCallExp>) {
((FeatureCallExp) astNode).setMarkedPre(true);
} else {
ERROR(variableExpCS.getIsMarkedPreCS(), "variableExpCS", OCLMessages.IllegalAtPre_ERROR_);//$NON-NLS-1$
}
}
if (!qualifiers.isEmpty()) {
if (astNode instanceof NavigationCallExp, ?>) {
@SuppressWarnings("unchecked")
NavigationCallExp callNode = (NavigationCallExp) astNode;
setQualifiers(env, "variableExpCS", callNode, qualifiers); //$NON-NLS-1$
} else if ((astNode instanceof LoopExp, ?>)
&& ((LoopExp, ?>) astNode).getBody() instanceof NavigationCallExp, ?>) {
// might have parsed an implicit collect expression
@SuppressWarnings("unchecked")
NavigationCallExp callNode = (NavigationCallExp) ((LoopExp) astNode)
.getBody();
setQualifiers(env, "variableExpCS", callNode, qualifiers);//$NON-NLS-1$
} else {
ERROR(variableExpCS, "variableExpCS", //$NON-NLS-1$
OCLMessages.bind(OCLMessages.IllegalQualifiers_ERROR_,
computeInputString(variableExpCS)));
}
} else if (astNode instanceof AssociationClassCallExp, ?>) {
@SuppressWarnings("unchecked")
AssociationClassCallExp callNode = (AssociationClassCallExp) astNode;
checkNotReflexive(env, "variableExpCS", callNode);//$NON-NLS-1$
}
return astNode;
}
/**
* Queries whether the specified call expression is adorned with
* {@literal @pre}.
*
* @param callExp
* a call expression
*
* @return whether the expression is marked pre
*
* @since 1.3
*/
protected boolean isAtPre(FeatureCallExpCS callExp) {
IsMarkedPreCS atPre = callExp.getIsMarkedPreCS();
return atPre != null;
}
/**
* Queries whether the specified variable expression is adorned with
* {@literal @pre}.
*
* @param variableExp
* a variable expression
*
* @return whether the expression is marked pre
*
* @since 1.3
*/
protected boolean isAtPre(VariableExpCS callExp) {
IsMarkedPreCS atPre = callExp.getIsMarkedPreCS();
return atPre != null;
}
/**
* Creates a variable expression with the variable that it references.
*
* @param env
* the current parsing environment
* @param cst
* the concrete syntax that produces the variable expression
* @param var
* the referred variable
*
* @return the variable expression
* @since 1.3
*/
protected VariableExp createVariableExp(
Environment env,
CSTNode cst, Variable var) {
VariableExp result = oclFactory.createVariableExp();
initASTMapping(env, result, cst, var);
if (var != null) {
result.setType(var.getType());
result.setReferredVariable(var);
result.setName(var.getName());
}
return result;
}
/**
* QualifiersCS
*
* @param arguments
* the OCLExpressionCS
arguments list
* @param env
* the OCL environment
* @param navigation
* @return the parsed OCLExpression
s list
*/
protected List> qualifiersCS(
List arguments,
Environment env,
OCLExpression navigation) {
if (arguments.isEmpty()) {
return Collections.emptyList();
}
List> qualifiers = new java.util.ArrayList>();
if (navigation instanceof LoopExp, ?>) {
@SuppressWarnings("unchecked")
LoopExp loopNode = (LoopExp) navigation;
navigation = loopNode.getBody();
}
if (navigation instanceof AssociationClassCallExp, ?>) {
@SuppressWarnings("unchecked")
AssociationClassCallExp acc = (AssociationClassCallExp) navigation;
OCLExpression source = acc.getSource();
OCLExpressionCS arg = arguments.get(0);
if (!(arg instanceof VariableExpCS)) {
ERROR(arg, "qualifiersCS",//$NON-NLS-1$
OCLMessages.bind(OCLMessages.ParseCSTNodeType_ERROR_,
"VariableExpCS",//$NON-NLS-1$
arg.eClass().getName()));
} else {
SimpleNameCS qualifier = ((VariableExpCS) arg)
.getSimpleNameCS();
String simpleName = qualifier.getValue();
C sourceType = source != null
? source.getType()
: null;
P property = lookupProperty(qualifier, env, sourceType,
simpleName);
if (property == null) {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedVar_ERROR_, simpleName);
ERROR(sourceType, "qualifiersCS", message);//$NON-NLS-1$
} else {
TRACE("qualifierCS", "Reference: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$
PropertyCallExp ref = oclFactory
.createPropertyCallExp();
initASTMapping(env, ref, arg);
ref.setReferredProperty(property);
ref.setType(getPropertyType(qualifier, env, sourceType,
property));
if (source == null) {
Variable implicitSource = env
.lookupImplicitSourceForProperty(simpleName);
VariableExp src = createVariableExp(env, arg,
implicitSource);
ref.setSource(src);
}
initStartEndPositions(ref, qualifier);
initPropertyPositions(ref, qualifier);
qualifiers.add(ref);
}
}
} else {
for (OCLExpressionCS arg : arguments) {
OCLExpression qualExp = oclExpressionCS(arg, env);
if (qualExp != null) {
qualifiers.add(qualExp);
}
}
}
return qualifiers;
}
/**
* IfExpCS
*
* @param ifExpCS
* the IfExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed IfExpCS
*/
protected IfExp ifExpCS(IfExpCS ifExpCS,
Environment env) {
OCLExpression condition = oclExpressionCS(ifExpCS.getCondition(),
env);
OCLExpression thenExpression = oclExpressionCS(ifExpCS
.getThenExpression(), env);
OCLExpression elseExpression = oclExpressionCS(ifExpCS
.getElseExpression(), env);
TRACE("ifExpCS", " "); //$NON-NLS-2$//$NON-NLS-1$
IfExp astNode = oclFactory.createIfExp();
if (isErrorNode(condition)) {
// don't validate the condition type
} else if (condition.getType() != getBoolean()) {
ERROR(ifExpCS.getCondition(), "ifExpCS", OCLMessages.bind( //$NON-NLS-1$
OCLMessages.BooleanForIf_ERROR_, computeInputString(ifExpCS
.getCondition())));
}
initASTMapping(env, astNode, ifExpCS);
astNode.setCondition(condition);
astNode.setThenExpression(thenExpression);
astNode.setElseExpression(elseExpression);
if ((thenExpression != null) && (elseExpression != null)) {
C commonType = getCommonSuperType(ifExpCS, "ifExpCS", env, //$NON-NLS-1$
thenExpression.getType(), elseExpression.getType());
astNode.setType(commonType);
if (isErrorNode(thenExpression)) {
// propagate error stigma to the if expression
markAsErrorNode(astNode);
}
if (isErrorNode(elseExpression)) {
// propagate error stigma to the if expression
markAsErrorNode(astNode);
}
} else {
astNode.setType(getOclVoid());
}
initStartEndPositions(astNode, ifExpCS);
return astNode;
}
/**
* LetExpCS
*
* @param letExpCS
* the LetExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed LetExpCS
*
* @deprecated Since 1.3, use {@link #letExp(LetExpCS, Environment)},
* instead.
*/
@SuppressWarnings("unchecked")
@Deprecated
protected LetExp letExpCS(LetExpCS letExpCS,
Environment env) {
return (LetExp) letExp(letExpCS, env);
}
/**
* LetExpCS
*
* @param letExpCS
* the LetExpCS
CSTNode
* @param env
* the OCL environment
*
* @return the parsed LetExpCS
*
* @since 1.3
*/
protected OCLExpression letExp(LetExpCS letExpCS,
Environment env) {
return letExpCSRecursive(letExpCS, 0, env);
}
/**
* Constructs the LetExp
*
* @param letExpCS
* the LetExpCS
CSTNode
* @param index
* the index of the VariableDeclarationCS to parse
* @param env
* the OCL environment
* @return the parsed OCLExpression
*/
protected OCLExpression letExpCSRecursive(LetExpCS letExpCS, int index,
Environment env) {
OCLExpression astNode;
if (index < letExpCS.getVariables().size()) {
VariableCS variableDeclarationCS = letExpCS.getVariables().get(
index);
Variable variableDeclaration = variableDeclarationCS(
variableDeclarationCS, env, true);
String varName = variableDeclaration.getName();
if (variableDeclaration.getType() == null) {
String message = OCLMessages.bind(
OCLMessages.DeclarationType_ERROR_, varName);
ERROR(variableDeclarationCS, "letExpCS", message);//$NON-NLS-1$
variableDeclaration.setType(getOclVoid());
}
if (variableDeclaration.getInitExpression() == null) {
String message = OCLMessages.bind(
OCLMessages.DeclarationNoInitExp_ERROR_, varName);
ERROR(variableDeclarationCS, "letExpCS", message);//$NON-NLS-1$
}
OCLExpression letSubExp = letExpCSRecursive(letExpCS, ++index,
env);
LetExp letExp = oclFactory.createLetExp();
initASTMapping(env, letExp, letExpCS);
letExp.setVariable(variableDeclaration);
letExp.setIn(letSubExp);
if (letSubExp != null) {
letExp.setType(letSubExp.getType());
} else {
letExp.setType(getOclVoid());
}
astNode = letExp;
env.deleteElement(varName);
} else {
astNode = oclExpressionCS(letExpCS.getInExpression(), env);
}
return astNode;
}
/**
* Parses a simpleNameCS token. This method is largely a
* template, delegating to helpers that may be separately overridden
* to resolve simple names to various kinds of expression.
*
* @param simpleNameCS
* the simpleNameCS
CSTNode
* @param env
* the OCL environment
* @param source
* the source of the simpleNameCS
* @return the parsed OCLExpression
*
* @see #simpleAssociationClassName(SimpleNameCS, Environment,
* OCLExpression, Object, String)
* @see #simplePropertyName(SimpleNameCS, Environment, OCLExpression,
* Object, String)
* @see #simpleTypeName(SimpleNameCS, Environment, OCLExpression, Object,
* String)
* @see #simpleVariableName(SimpleNameCS, Environment, OCLExpression,
* String)
* @see #simpleUndefinedName(SimpleNameCS, Environment, OCLExpression,
* String)
*/
protected OCLExpression simpleNameCS(SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source) {
if ((source != null) && isErrorNode(source)) {
// don't attempt to parse navigation from an unparseable source
return source; // return the same unparseable token
}
String simpleName = null;
C classifier = null;
/*
* A name can be a variable defined by a Variable declaration, the
* special variable "self", an attribute or a reference to another
* object. If the source is not null, then the last token was a "." or
* "->" The source is used to establish the navigation. If no type is
* provided, then either the name is a the use of a variable, or there
* is an implicit variable declaration (self or an iterator) that is the
* source.
*/
switch (simpleNameCS.getType().getValue()) {
case SimpleTypeEnum.SELF :
case SimpleTypeEnum.KEYWORD :
case SimpleTypeEnum.IDENTIFIER :
simpleName = simpleNameCS.getValue();
break;
case SimpleTypeEnum.INTEGER :
case SimpleTypeEnum.STRING :
case SimpleTypeEnum.REAL :
case SimpleTypeEnum.BOOLEAN :
case SimpleTypeEnum.OCL_ANY :
case SimpleTypeEnum.OCL_VOID :
case SimpleTypeEnum.OCL_INVALID :
case SimpleTypeEnum.OCL_MESSAGE :
case SimpleTypeEnum.UNLIMITED_NATURAL :
// if we have a source, then this is a feature call
if (source == null) {
classifier = primitiveTypeCS(simpleNameCS.getType(), env);
simpleName = uml.getName(classifier);
}
break;
}
/*
* The source may be a collection type (for example, in
* self.children.name, children may be a set.)_ In this case, we have to
* get the element type of children, so that the attribute name can be
* found. The source type can also be a tuple type. In this case, we
* need to get the EClass of the tuple.
*/
C sourceElementType = null;
if (source != null) {
sourceElementType = source.getType();
if (sourceElementType instanceof CollectionType, ?>) {
@SuppressWarnings("unchecked")
CollectionType ct = (CollectionType) sourceElementType;
sourceElementType = ct.getElementType();
}
}
// cascaded alternatives for a simpleNameCS
OCLExpression astNode = simpleTypeName(simpleNameCS, env, source,
classifier, simpleName);
if (astNode == null) {
astNode = simpleVariableName(simpleNameCS, env, source, simpleName);
}
if (astNode == null) {
astNode = simpleNavigationName(simpleNameCS, env, source,
sourceElementType, simpleName);
}
if (astNode == null) {
astNode = simpleAssociationClassName(simpleNameCS, env, source,
sourceElementType, simpleName);
}
if (astNode == null) {
astNode = simpleUndefinedName(simpleNameCS, env, source, simpleName);
}
/*
* If the source type is a collection, then need there is an implicit
* COLLECT operator. Note that this rule is not called after "->". Check
* for FeatureCallExp in case we created a dummy InvalidLiteralExp.
*/
if ((source != null) && (source.getType() instanceof CollectionType, ?>)
&& (astNode instanceof FeatureCallExp>)) {
astNode = createImplicitCollect(source,
(FeatureCallExp) astNode, env, simpleNameCS);
}
return astNode;
}
/**
* Attempts to parse a simpleNameCS as an association-class call
* expression.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, or null
if the
* source is implicit
* @param owner
* the owner of the association-class end to be navigated, or
* null
if the source is implicit
* @param simpleName
* the simple name, as a string
* @return the parsed association-class call, or null
if the
* simple name does not resolve to a related association class
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected AssociationClassCallExp simpleAssociationClassName(
SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, C owner, String simpleName) {
AssociationClassCallExp result = null;
C assocClass = lookupAssociationClassReference(simpleNameCS, env,
owner, simpleName);
if (assocClass != null) {
TRACE("variableExpCS", "Association class: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$
result = oclFactory.createAssociationClassCallExp();
initASTMapping(env, result, simpleNameCS);
result.setReferredAssociationClass(assocClass);
if (source != null) {
result.setSource(source);
} else {
Variable implicitSource = env
.lookupImplicitSourceForAssociationClass(simpleName);
VariableExp src = createVariableExp(env, simpleNameCS,
implicitSource);
result.setSource(src);
}
// infer the navigation source and type of the association class
// call expression from the association class end that is
// implicitly navigated (in case it is not explicit as a qualifier)
C acrefType = null;
C sourceType = getElementType(result.getSource().getType());
List ends = uml.getMemberEnds(assocClass);
List
available = uml.getAttributes(sourceType);
for (P end : ends) {
if (available.contains(end)) {
// this is the navigation source
result.setNavigationSource(end);
CollectionKind kind = getCollectionKind(getOCLType(env, end));
if (kind != null) {
acrefType = getCollectionType(simpleNameCS, env, kind,
assocClass);
} else {
acrefType = assocClass;
}
}
}
if (acrefType == null) {
// for non-navigable association classes, assume set type
acrefType = getSetType(simpleNameCS, env, assocClass);
}
result.setType(acrefType);
initPropertyPositions(result, simpleNameCS);
}
return result;
}
/**
* Allows subclasses to return calls that are not necessarily a {@link PropertyCallExp} but some
* other {@link NavigationCallExp}.
*
* @since 3.1
*/
protected NavigationCallExp simpleNavigationName(
SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, C owner, String simpleName) {
return simplePropertyName(simpleNameCS, env, source, owner, simpleName);
}
/**
* Attempts to parse a simpleNameCS as a property call expression.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, or null
if the
* source is implicit
* @param owner
* the owner of the property to be navigated, or
* null
if the source is implicit
* @param simpleName
* the simple name, as a string
* @return the parsed property call, or null
if the simple name
* does not resolve to an available property
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected PropertyCallExp simplePropertyName(
SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, C owner, String simpleName) {
if (simpleName == null) {
return null;
}
PropertyCallExp result = null;
P property = lookupProperty(simpleNameCS, env, owner, simpleName);
if (property != null) {
TRACE("variableExpCS", "Property: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$
result = oclFactory.createPropertyCallExp();
initASTMapping(env, result, simpleNameCS, null);
result.setReferredProperty(property);
result.setType(getPropertyType(simpleNameCS, env, owner, property));
if (source != null) {
result.setSource(source);
} else {
Variable implicitSource = env
.lookupImplicitSourceForProperty(simpleName);
VariableExp src = createVariableExp(env, simpleNameCS,
implicitSource);
result.setSource(src);
}
initPropertyPositions(result, simpleNameCS);
}
return result;
}
/**
* Attempts to parse a simpleNameCS as a type expression. For the
* sake of completeness and generality, information about a navigation
* source
is provided, if any. In such cases, it is usually
* inappropriate to attempt to resolve the simple name as a type expression.
* Also, the referenced classifier
may already be determined to
* be a member of the OCL Standard Library.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, if any, in which case this
* would not be a type expression in OCL
* @param classifier
* the referenced classifier, if it is already known to be one of
* the OCL Standard Library types
* @param simpleName
* the simple name, as a string
* @return the parsed type expression, or null
if the simple
* name does not resolve to an accessible type
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected TypeExp simpleTypeName(SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, C classifier, String simpleName) {
if (simpleNameCS.getAst() != null) {
// Non-null when invoked from variableExpCS, so there is
// no point trying to turn the variable name into a type
return null;
}
TypeExp result = null;
// if we have a source, then this is a feature call
if ((classifier == null) && (source == null)) {
classifier = lookupClassifier(simpleNameCS, env, Collections
.singletonList(simpleName));
}
if (classifier != null) {
/*
* Variable is a classifier. Classifiers are used in allInstances,
* isKindOf, isTypeOf, asTypeOf operations
*/
result = typeCS(simpleNameCS, env, classifier);
}
return result;
}
/**
* Attempts to parse a simpleNameCS as a variable expression. The
* navigation source
, if any, is provided for completeness and
* generality. If there is a navigation source, then it is not usually
* appropriate to attempt to resolve the simple name as a variable.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, if any, in which case this
* would not be a variable expression in OCL
* @param simpleName
* the simple name, as a string
* @return the parsed variable call, or null
if the simple name
* does not resolve to an accessible variable
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected VariableExp simpleVariableName(SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, String simpleName) {
VariableExp result = null;
if (source == null) {
Variable vdcl = env.lookup(simpleName);
if (vdcl != null) {
// Either a use of a declared variable or self
TRACE("variableExpCS", "Variable Expression: " + simpleName);//$NON-NLS-2$//$NON-NLS-1$
result = createVariableExp(env, simpleNameCS, vdcl);
}
}
return result;
}
/**
* The error case for simpleNameCS, which is called when the name
* cannot be resolved to any suitable expression. The result is a
* {@linkplain #createDummyInvalidLiteralExp(Environment, CSTNode) dummy}
* expression.
*
* @param simpleNameCS
* the simple name
* @param env
* the current environment
* @param source
* the navigation source expression, or null
if the
* source is implicit
* @param simpleName
* the simple name, as a string
* @return the dummy expression that is a placeholder for the unresolved
* reference
*
* @see #simpleNameCS(SimpleNameCS, Environment, OCLExpression)
*
* @since 1.3
*/
protected OCLExpression simpleUndefinedName(SimpleNameCS simpleNameCS,
Environment env,
OCLExpression source, String simpleName) {
if ((source != null) && (env.lookup(simpleName)) != null) {
String message = OCLMessages.bind(OCLMessages.VarInNavExp_ERROR_,
simpleName);
ERROR(simpleNameCS, "variableExpCS", message);//$NON-NLS-1$
} else {
String message = OCLMessages.bind(
OCLMessages.UnrecognizedVar_ERROR_, simpleName);
ERROR(simpleNameCS, "variableExpCS", message);//$NON-NLS-1$
}
return createDummyInvalidLiteralExp(env, simpleNameCS);
}
/**
* Creates an implicit collect
iterator expression for a
* property call on a collection-type source expression.
*
* @param source
* the property call source expression
* @param propertyCall
* the property call expression
* @param env
* the current environment
*
* @return the collect expression
*/
protected IteratorExp createImplicitCollect(OCLExpression source,
FeatureCallExp propertyCall,
Environment env,
CSTNode cstNode) {
@SuppressWarnings("unchecked")
C sourceElementType = ((CollectionType) source.getType())
.getElementType();
IteratorExp result = oclFactory.createIteratorExp();
initASTMapping(env, result, cstNode, null);
Variable itervar = genVariableDeclaration(cstNode,
"modelPropertyCallCS", env,//$NON-NLS-1$
null, sourceElementType, null, false, true, false);
List> iters = result.getIterator();
iters.add(itervar);
result.setBody(propertyCall);
result.setName("collect");//$NON-NLS-1$
VariableExp vexp = createVariableExp(env, cstNode, itervar);
/*
* adjust the source variable for the body expression to be the newly
* generated implicit iterator variable
*/
propertyCall.setSource(vexp);
if (!(propertyCall instanceof OperationCallExp, ?>)) {
// the overall start and end positions are the property positions
propertyCall.setStartPosition(propertyCall
.getPropertyStartPosition());
propertyCall.setEndPosition(propertyCall.getPropertyEndPosition());
}
result.setSource(source);
// the result of a collect() is flattened, so if the value
// that we are collecting is a Collection type, the resulting
// type must be flattened by taking its element type (recursively)
C bodyType = propertyCall.getType();
if (bodyType instanceof CollectionType, ?>) {
@SuppressWarnings("unchecked")
CollectionType ct = (CollectionType) bodyType;
bodyType = CollectionUtil.getFlattenedElementType(ct);
}
if (source.getType() instanceof SequenceType, ?>
|| source.getType() instanceof OrderedSetType, ?>) {
C c = getCollectionType(cstNode, env,
CollectionKind.SEQUENCE_LITERAL, bodyType);
result.setType(c);
} else {
C c = getCollectionType(cstNode, env, CollectionKind.BAG_LITERAL,
bodyType);
result.setType(c);
}
env.deleteElement(itervar.getName());
return result;
}
/**
* PrimitiveTypeCS
*
* @param simpleType
* the SimpleTypeEnum
representing the primitive
* type
* @return an EClassifie
representing the primitive type
*/
protected C primitiveTypeCS(SimpleTypeEnum simpleType,
Environment env) {
C astNode = null;
switch (simpleType) {
case INTEGER_LITERAL :
astNode = env.getOCLStandardLibrary().getInteger();
break;
case UNLIMITED_NATURAL_LITERAL :
astNode = env.getOCLStandardLibrary().getUnlimitedNatural();
break;
case STRING_LITERAL :
astNode = env.getOCLStandardLibrary().getString();
break;
case REAL_LITERAL :
astNode = env.getOCLStandardLibrary().getReal();
break;
case BOOLEAN_LITERAL :
astNode = env.getOCLStandardLibrary().getBoolean();
break;
case OCL_ANY_LITERAL :
astNode = env.getOCLStandardLibrary().getOclAny();
break;
case OCL_VOID_LITERAL :
astNode = env.getOCLStandardLibrary().getOclVoid();
break;
case OCL_INVALID_LITERAL :
astNode = env.getOCLStandardLibrary().getOclInvalid();
break;
case OCL_MESSAGE_LITERAL :
astNode = env.getOCLStandardLibrary().getOclMessage();
break;
}
return astNode;
}
/**
* PrimitiveLiteralExpCS
*
* @param primitiveLiteralExpCS
* the PrimitiveLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed LiteralExp
*/
protected LiteralExp primitiveLiteralExpCS(
PrimitiveLiteralExpCS primitiveLiteralExpCS,
Environment env) {
LiteralExp astNode = null;
if (primitiveLiteralExpCS instanceof IntegerLiteralExpCS) {
astNode = integerLiteralExpCS(
(IntegerLiteralExpCS) primitiveLiteralExpCS, env);
} else if (primitiveLiteralExpCS instanceof UnlimitedNaturalLiteralExpCS) {
astNode = unlimitedNaturalLiteralExpCS(
(UnlimitedNaturalLiteralExpCS) primitiveLiteralExpCS, env);
} else if (primitiveLiteralExpCS instanceof RealLiteralExpCS) {
astNode = realLiteralExpCS(
(RealLiteralExpCS) primitiveLiteralExpCS, env);
} else if (primitiveLiteralExpCS instanceof StringLiteralExpCS) {
astNode = stringLiteralExpCS(
(StringLiteralExpCS) primitiveLiteralExpCS, env);
} else if (primitiveLiteralExpCS instanceof BooleanLiteralExpCS) {
astNode = booleanLiteralExpCS(
(BooleanLiteralExpCS) primitiveLiteralExpCS, env);
}
return astNode;
}
/**
* IntegerLiteralExpCS
*
* @param integerLiteralExpCS
* the IntegerLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed IntegerLiteralExp
*/
protected IntegerLiteralExp integerLiteralExpCS(
IntegerLiteralExpCS integerLiteralExpCS,
Environment env) {
IntegerLiteralExp astNode = oclFactory.createIntegerLiteralExp();
initASTMapping(env, astNode, integerLiteralExpCS);
astNode.setIntegerSymbol(integerLiteralExpCS.getIntegerSymbol());
astNode.setType(env.getOCLStandardLibrary().getInteger());
TRACE(
"integerLiteralExpCS", "Integer: " + integerLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* UnlimitedNaturalLiteralExpCS
*
* @param unlimitedNaturalLiteralExpCS
* the UnlimitedNaturalLiteralExpCS
* CSTNode
* @param env
* the OCL environment
* @return the parsed UnlimitedNaturalLiteralExp
*/
protected UnlimitedNaturalLiteralExp unlimitedNaturalLiteralExpCS(
UnlimitedNaturalLiteralExpCS unlimitedNaturalLiteralExpCS,
Environment env) {
UnlimitedNaturalLiteralExp astNode = oclFactory
.createUnlimitedNaturalLiteralExp();
initASTMapping(env, astNode, unlimitedNaturalLiteralExpCS);
astNode.setIntegerSymbol(unlimitedNaturalLiteralExpCS
.getIntegerSymbol());
astNode.setType(env.getOCLStandardLibrary().getUnlimitedNatural());
TRACE(
"unlimitedNaturalLiteralExpCS", "UnlimitedNatural: " + unlimitedNaturalLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* RealLiteralExpCS
*
* @param realLiteralExpCS
* the RealLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed RealLiteralExp
*/
protected RealLiteralExp realLiteralExpCS(
RealLiteralExpCS realLiteralExpCS,
Environment env) {
RealLiteralExp astNode = oclFactory.createRealLiteralExp();
initASTMapping(env, astNode, realLiteralExpCS);
astNode.setRealSymbol(realLiteralExpCS.getRealSymbol());
astNode.setType(env.getOCLStandardLibrary().getReal());
TRACE("realLiteralExpCS", "Real: " + realLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* StringLiteralExpCS
*
* @param stringLiteralExpCS
* the StringLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed StringLiteralExp
*/
protected StringLiteralExp stringLiteralExpCS(
StringLiteralExpCS stringLiteralExpCS,
Environment env) {
StringLiteralExp astNode = oclFactory.createStringLiteralExp();
initASTMapping(env, astNode, stringLiteralExpCS);
astNode.setStringSymbol(stringLiteralExpCS.getUnescapedStringSymbol());
astNode.setType(env.getOCLStandardLibrary().getString());
TRACE("stringLiteralExpCS", "String: " + stringLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* BooleanLiteralExpCS
*
* @param booleanLiteralExpCS
* the BooleanLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed BooleanLiteralExp
*/
protected BooleanLiteralExp booleanLiteralExpCS(
BooleanLiteralExpCS booleanLiteralExpCS,
Environment env) {
BooleanLiteralExp astNode = oclFactory.createBooleanLiteralExp();
initASTMapping(env, astNode, booleanLiteralExpCS);
astNode.setBooleanSymbol(booleanLiteralExpCS.getBooleanSymbol());
astNode.setType(env.getOCLStandardLibrary().getBoolean());
TRACE(
"booleanLiteralExpCS", "Boolean: " + booleanLiteralExpCS.getSymbol());//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* NullLiteralExpCS
*
* @param nullLiteralExpCS
* the NullLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed NullLiteralExp
*/
protected NullLiteralExp nullLiteralExpCS(
NullLiteralExpCS nullLiteralExpCS,
Environment env) {
NullLiteralExp astNode = oclFactory.createNullLiteralExp();
initASTMapping(env, astNode, nullLiteralExpCS);
astNode.setType(env.getOCLStandardLibrary().getOclVoid());
TRACE("nullLiteralExpCS", "OclVoid: null");//$NON-NLS-2$//$NON-NLS-1$
return astNode;
}
/**
* InvalidLiteralExpCS
*
* @param invalidLiteralExpCS
* the InvalidLiteralExpCS
CSTNode
* @param env
* the OCL environment
* @return the parsed InvalidLiteralExp
*/
protected InvalidLiteralExp invalidLiteralExpCS(
InvalidLiteralExpCS invalidLiteralExpCS,
Environment env) {
InvalidLiteralExp