/*******************************************************************************
* Copyright (c) 2007, 2016 Borland Software 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:
* Borland Software Corporation - initial API and implementation
* Christopher Gerking - bugs 390088, 477331
* Alex Paperno - bugs 416584, 401521, 403440, 424584
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.ast.env;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.impl.EPackageRegistryImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.m2m.internal.qvt.oml.NLS;
import org.eclipse.m2m.internal.qvt.oml.QvtMessage;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtEnvironmentBase.CollisionStatus.CollisionKind;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.HiddenElementAdapter;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.MappingsMapKey;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalTypesUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.ValidationMessages;
import org.eclipse.m2m.internal.qvt.oml.compiler.QvtCompilerOptions;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveInExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.LookupException;
import org.eclipse.ocl.TypeChecker;
import org.eclipse.ocl.TypeResolver;
import org.eclipse.ocl.cst.CSTNode;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreFactory;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.lpg.AbstractLexer;
import org.eclipse.ocl.lpg.AbstractParser;
import org.eclipse.ocl.lpg.AbstractProblemHandler;
import org.eclipse.ocl.lpg.ProblemHandler;
import org.eclipse.ocl.lpg.ProblemHandler.Phase;
import org.eclipse.ocl.lpg.ProblemHandler.Severity;
import org.eclipse.ocl.parser.AbstractOCLAnalyzer;
import org.eclipse.ocl.util.TypeUtil;
import org.eclipse.ocl.utilities.ASTNode;
import org.eclipse.ocl.utilities.TypedElement;
import org.eclipse.ocl.utilities.UMLReflection;
public class QvtOperationalEnv extends QvtEnvironmentBase { //EcoreEnvironment {
public static final String THIS = "this"; //$NON-NLS-1$
public static final String MAIN = "main"; //$NON-NLS-1$
public static final String TAG_ALIAS = "alias"; //$NON-NLS-1$
public static final String METAMODEL_COMPLIANCE_KIND_STRICT = "strict"; //$NON-NLS-1$
private EPackage.Registry myPackageRegistry;
private final Set<QvtMessage> myWarningSet = new LinkedHashSet<QvtMessage>(2);
private final Set<QvtMessage> myErrorSet = new LinkedHashSet<QvtMessage>(2);
private boolean myCheckForDuplicateErrors;
private QvtCompilerOptions myCompilerOptions;
private boolean myParentLocal = false;
private boolean myIsWithinInitMappingSection = false;
private final Map<String, ModelType> myModelTypeRegistry;
private final Map<List<String>, EClassifier> fRegisteredClassifiers;
private final Map<MappingsMapKey, List<MappingOperation>> myMappingsMap = new HashMap<MappingsMapKey, List<MappingOperation>>();
private final Map<ResolveInExp, MappingsMapKey> myResolveInExps = new HashMap<ResolveInExp, MappingsMapKey>();
private final LookupPackageableElementDelegate<EClassifier> LOOKUP_CLASSIFIER_DELEGATE = new LookupPackageableElementDelegate<EClassifier>() {
public EClassifier lookupPackageableElement(List<String> names) {
return QvtOperationalEnv.super.lookupClassifier(names);
}
};
private final LookupPackageableElementDelegate<EPackage> LOOKUP_PACKAGE_DELEGATE = new LookupPackageableElementDelegate<EPackage>() {
public EPackage lookupPackageableElement(List<String> names) {
return QvtOperationalEnv.super.lookupPackage(names);
}
};
protected QvtOperationalEnv(QvtOperationalEnv parent) {
super(parent);
if(parent == null) {
throw new IllegalArgumentException("Non-null parent QVT environment expected"); //$NON-NLS-1$
}
myCheckForDuplicateErrors = false;
myModelTypeRegistry = parent.myModelTypeRegistry;
myCompilerOptions = parent.myCompilerOptions;
fRegisteredClassifiers = parent.fRegisteredClassifiers;
}
protected QvtOperationalEnv(EPackage.Registry packageRegistry, Resource resource) {
super(packageRegistry, resource);
myPackageRegistry = packageRegistry;
myModelTypeRegistry = new LinkedHashMap<String, ModelType>(1);
fRegisteredClassifiers = new HashMap<List<String>, EClassifier>();
}
/**
* This operation declares the parameters of the given operation
*/
@Override
protected void setContextOperation(EOperation operation) {
super.setContextOperation(operation);
defineOperationParameters(operation);
}
@Override
protected TypeResolver<EClassifier, EOperation, EStructuralFeature> createTypeResolver(Resource resource) {
return new QvtTypeResolverImpl(this, resource);
}
@Override
protected TypeChecker<EClassifier, EOperation, EStructuralFeature> createTypeChecker() {
return new TypeCheckerImpl(this);
}
/**
* Gets the package registry used in this environment EClassifier lookup.
*/
public EPackage.Registry getEPackageRegistry() {
if(myPackageRegistry != null) {
return myPackageRegistry;
}
EPackage.Registry registry;
if(getInternalParent() == null) {
if(getFactory() != null) {
registry = getFactory().getEPackageRegistry();
} else {
myPackageRegistry = registry = createDefaultPackageRegistry();
}
} else {
QvtOperationalEnv parentEnv = (QvtOperationalEnv) getInternalParent();
registry = parentEnv.getEPackageRegistry();
}
return registry;
}
public Map<String, ModelType> getModelTypeRegistry() {
return myModelTypeRegistry;
}
@Override
public QvtOperationalEnvFactory getFactory() {
EnvironmentFactory<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject>
superFactory = super.getFactory();
if(superFactory instanceof QvtOperationalEnvFactory == true) {
// I have an explicitly assigned creating factory already
return (QvtOperationalEnvFactory) superFactory;
}
// no factory or the default Ecore factory instantiated by OCL environment
QvtOperationalEnvFactory qvtFactory = new QvtOperationalEnvFactory(getEPackageRegistry());
setFactory(qvtFactory);
return qvtFactory;
}
@SuppressWarnings("deprecation")
@Override
protected void setFactory(EnvironmentFactory<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral,
EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> factory) {
if(factory instanceof QvtOperationalEnvFactory == false) {
throw new IllegalArgumentException("QVT EnvFactory required"); //$NON-NLS-1$
}
super.setFactory(factory);
}
public void setParentLocal() {
myParentLocal = true;
}
public void enterInitMappingSection() {
myIsWithinInitMappingSection = true;
}
public void exitInitMappingSection() {
myIsWithinInitMappingSection = false;
}
public boolean isWithinInitMappingSection() {
return myIsWithinInitMappingSection;
}
@Override
public Variable<EClassifier, EParameter> lookupLocal(String name) {
// support operation parameters whose names need to be escaped in OCL
Variable<EClassifier, EParameter> result = super.lookupLocal(name);
if (result == null && myParentLocal) {
result = getInternalParent().lookupLocal(name);
}
return result;
}
public EStructuralFeature lookupPropertyAlias(EClassifier owner, String aliasName) {
Module module = getModuleContextType();
if(module != null) {
for (EAnnotation nextTag : module.getOwnedTag()) {
String newName = nextTag.getDetails().get(TAG_ALIAS);
EList<EObject> references = nextTag.getReferences();
EObject element = references.isEmpty() ? null : references.get(0);
if(aliasName.equals(newName) && element instanceof EStructuralFeature) {
if(TypeUtil.compatibleTypeMatch(this, owner, getUMLReflection().getOwningClassifier(element))) {
return (EStructuralFeature)element;
}
}
}
}
return null;
}
@Override
public EOperation tryLookupOperation(EClassifier owner, String name, List<? extends TypedElement<EClassifier>> args) throws LookupException {
EOperation result = doLookupOperation(owner, name, args);
if ((result == null) && AbstractOCLAnalyzer.isEscaped(name)) {
result = doLookupOperation(owner, AbstractOCLAnalyzer.unescape(name), args);
}
return result;
}
private EOperation doLookupOperation(EClassifier owner, String name, List<? extends TypedElement<EClassifier>> args) throws LookupException {
if (owner == null) {
Variable<EClassifier, EParameter> vdcl = lookupImplicitSourceForOperation(name, args);
if (vdcl == null) {
return null;
}
owner = vdcl.getType();
}
TypeChecker<EClassifier, EOperation, EStructuralFeature> typeChecker = getTypeChecker();
if(typeChecker instanceof TypeCheckerImpl) {
return ((TypeCheckerImpl)typeChecker).findMostSpecificOperationMatching(owner, name, args);
}
return TypeUtil.findOperationMatching(this, owner, name, args);
}
public List<MappingOperation> lookupMappingOperations(EClassifier owner, String name) {
if (owner == null) {
owner = getModuleContextType();
if(owner == null) {
return Collections.emptyList();
}
}
UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> uml = getUMLReflection();
List<EOperation> operations = TypeUtil.getOperations(this, owner);
List<MappingOperation> result = new ArrayList<MappingOperation>();
for (EOperation operation : operations) {
if (uml.getName(operation).equals(name) && QvtOperationalUtil.isMappingOperation(operation)) {
result.add((MappingOperation)operation);
}
}
return result;
}
public EOperation tryLookupConstructorOperation(EClassifier owner, String name, List<? extends TypedElement<EClassifier>> args) throws LookupException {
List<EOperation> operations = lookupConstructorOperations(owner, name, args);
if(operations == null || operations.isEmpty()) {
return null;
}
TypeChecker<EClassifier, EOperation, EStructuralFeature> typeChecker = getTypeChecker();
if(typeChecker instanceof TypeCheckerImpl) {
TypeCheckerImpl specificChecker = (TypeCheckerImpl) typeChecker;
return specificChecker.getMostSpecificOperation(operations, args);
}
// fall back implementation in case some overrides #createTypeChecker()
// first pass with strict matching (like in doFindCollidingOperation() and QvtOperationalEnv::lookupOperation() for mappings)
for (EOperation op : operations) {
List<EParameter> params = op.getEParameters();
boolean isMatched = true;
for (int i = 0, n = params.size(); i < n; ++i) {
if (!TypeUtil.exactTypeMatch(this, getUMLReflection().getOCLType(args.get(i)),
getUMLReflection().getOCLType(params.get(i)))) {
isMatched = false;
break;
}
}
if (isMatched) {
return op;
}
}
// second pass with conformance matching (like in AbstractEnvironment::lookupOperation() for operations)
for (EOperation op : operations) {
if (getTypeChecker().matchArgs(owner, op.getEParameters(), args)) {
return op;
}
}
return null;
}
private List<EOperation> lookupConstructorOperations(EClassifier owner, String name, List<? extends TypedElement<EClassifier>> args) {
if (owner == null) {
owner = getModuleContextType();
if(owner == null) {
return Collections.emptyList();
}
}
UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> uml = getUMLReflection();
TypeChecker<EClassifier, EOperation, EStructuralFeature> typeChecker = getTypeChecker();
List<EOperation> operations = typeChecker.getOperations(owner);
List<EOperation> result = new ArrayList<EOperation>(2);
for (EOperation operation : operations) {
if (uml.getName(operation).equals(name)
&& QvtOperationalUtil.isConstructorOperation(operation)
&& typeChecker.matchArgs(owner, uml.getParameters(operation), args)) {
EClassifier nextOwner = uml.getOwningClassifier(operation);
// for constructors consider only identical types
if(owner == nextOwner) {
result.add(operation);
}
}
}
return result;
}
public QvtMessage reportError(String message, int startOffset, int endOffset) {
if ((myCompilerOptions != null) && !myCompilerOptions.isReportErrors()) {
return null;
}
QvtOperationalEnv parent = this;
while (parent.getInternalParent() != null) {
parent = (QvtOperationalEnv) parent.getInternalParent();
}
if (parent instanceof QvtOperationalModuleEnv && ((QvtOperationalModuleEnv)parent).getFileParent() != null) {
parent = ((QvtOperationalModuleEnv)parent).getFileParent();
}
boolean foundSameLocation = false;
int msgLength = endOffset-startOffset+1;
if (myCheckForDuplicateErrors) {
for (QvtMessage msg : parent.myErrorSet) {
if (msg.getOffset() == startOffset && msg.getLength() == msgLength) {
foundSameLocation = true;
return msg;
}
}
}
QvtMessage error = new QvtMessage(message, QvtMessage.SEVERITY_ERROR, startOffset, msgLength, getLineNum(parent, startOffset));
if (!foundSameLocation) {
parent.myErrorSet.add(error);
}
return error;
}
public QvtMessage reportWarning(String message, int startOffset, int endOffset) {
if ((myCompilerOptions != null) && !myCompilerOptions.isReportErrors()) {
return null;
}
QvtOperationalEnv parent = this;
while (parent.getInternalParent() != null) {
parent = (QvtOperationalEnv) parent.getInternalParent();
}
if (parent instanceof QvtOperationalModuleEnv && ((QvtOperationalModuleEnv)parent).getFileParent() != null) {
parent = ((QvtOperationalModuleEnv)parent).getFileParent();
}
QvtMessage warning = new QvtMessage(message, QvtMessage.SEVERITY_WARNING, startOffset, endOffset-startOffset+1, getLineNum(parent, startOffset));
parent.myWarningSet.add(warning);
return warning;
}
public QvtMessage reportError(String message, CSTNode node) {
int startOffset = (node != null) ? node.getStartOffset() : 0;
int endOffset = (node != null) ? node.getEndOffset() : 0;
return reportError(message, startOffset, endOffset);
}
public QvtMessage reportWarning(String message, CSTNode node) {
int startOffset = (node != null) ? node.getStartOffset() : 0;
int endOffset = (node != null) ? node.getEndOffset() : 0;
return reportWarning(message, startOffset, endOffset);
}
public boolean hasErrors() {
return myErrorSet != null && myErrorSet.isEmpty() == false;
}
public boolean hasWarnings() {
return myWarningSet != null && myWarningSet.isEmpty() == false;
}
public List<QvtMessage> getErrorsList() {
return new ArrayList<QvtMessage>(myErrorSet);
}
public List<QvtMessage> getWarningsList() {
return new ArrayList<QvtMessage>(myWarningSet);
}
public List<QvtMessage> getAllProblemMessages() {
if(hasErrors() || hasWarnings()) {
List<QvtMessage> result = new ArrayList<QvtMessage>();
result.addAll(getErrorsList());
result.addAll(getWarningsList());
return result;
}
return Collections.emptyList();
}
public void clearProblems() {
if(myErrorSet != null) {
myErrorSet.clear();
}
if(myWarningSet != null) {
myWarningSet.clear();
}
}
/**
* Register given modeltype in the Environment. Modeltype's registry is used in
* override {@link #lookupPackage(List)} and {@link #lookupClassifier(List)}
* @return the previously registered model-type if any, otherwise <code>null</code>
*/
public ModelType registerModelType(ModelType modelType) {
return myModelTypeRegistry.put(modelType.getName(), modelType);
}
public ModelType getModelType(String name) {
return myModelTypeRegistry.get(name);
}
@Override
public Variable<EClassifier, EParameter> lookupImplicitSourceForOperation(
String name, List<? extends TypedElement<EClassifier>> args) {
// propagate implict source lookup to parent, allowing to reach the module-wide 'this'
Variable<EClassifier, EParameter> result = super.lookupImplicitSourceForOperation(name, args);
Variable<EClassifier, EParameter> tentativeResult = result;
// check if implicit source results in self variable, try lookup for implicit this as a higher precedence
// Remark: validation should report the problem about call on 'self' using implicit source
if((result == null || SELF_VARIABLE_NAME.equals(result.getName())) && getInternalParent() != null) {
result = getInternalParent().lookupImplicitSourceForOperation(name, args);
if(tentativeResult != null && result == null) {
result = tentativeResult;
}
}
return result;
}
@Override
public Variable<EClassifier, EParameter> lookupImplicitSourceForProperty(String name) {
Variable<EClassifier, EParameter> implicitSource = super.lookupImplicitSourceForProperty(name);
Variable<EClassifier, EParameter> tentativeResult = implicitSource;
// check if implicit source results in self variable, try lookup for implicit this as a higher precedence
// Remark: validation should report the problem about call on 'self' using implicit source
if((implicitSource == null || SELF_VARIABLE_NAME.equals(implicitSource.getName())) && getInternalParent() != null) {
implicitSource = getInternalParent().lookupImplicitSourceForProperty(name);
if(tentativeResult != null && implicitSource == null) {
implicitSource = tentativeResult;
}
}
return implicitSource;
}
@Override
public EClassifier lookupClassifier(List<String> names) {
if(names.isEmpty()) {
return null;
}
if (fRegisteredClassifiers.containsKey(names)) {
return fRegisteredClassifiers.get(names);
}
EClassifier eClassifier = lookupClassifierImpl(names);
fRegisteredClassifiers.put(names, eClassifier);
return eClassifier;
}
private EClassifier lookupClassifierImpl(List<String> names) {
String firstName = names.get(0);
if (names.size() == 1) {
// Unqualified type. lookup rules:
// - Firstly a type definition existing at the level of the current module (a transformation or a library) is searched.
// - If not found, all the packages of the model types declared in the module are recursively visited to found a type with the same name.
// TODO support intermediate classes (hosted in implicit '_INTERMEDIATE' package)
Module moduleContextType = getModuleContextType();
if(moduleContextType != null && firstName.equals(moduleContextType.getName())) {
return moduleContextType;
}
for (QvtEnvironmentBase nextImported : getRootEnv().getImportsByExtends()) {
Module importedModule = nextImported.getModuleContextType();
if(importedModule != null && firstName.equals(importedModule.getName())) {
return importedModule;
}
}
for (QvtEnvironmentBase nextImported : getRootEnv().getImportsByAccess()) {
Module importedModule = nextImported.getModuleContextType();
if(importedModule != null && firstName.equals(importedModule.getName())) {
return importedModule;
}
}
if (myModelTypeRegistry.containsKey(firstName)) {
return myModelTypeRegistry.get(firstName);
}
for (ModelType modelType : myModelTypeRegistry.values()) {
EClassifier lookupClassifier = doLookupModeltypeClassifier(modelType, names);
if (lookupClassifier != null) {
return lookupClassifier;
}
}
}
if (names.size() > 1) {
// Qualified type
// It is possible to either qualify the type name with a model type or a package name
if(names.size() == 2) {
EClassifier stdType = QvtOperationalStdLibrary.INSTANCE.lookupClassifier(names);
if(stdType != null) {
return stdType;
}
}
if (myModelTypeRegistry.containsKey(firstName)) {
EClassifier lookupClassifier = doLookupModeltypeClassifier(
myModelTypeRegistry.get(firstName), names.subList(1, names.size()));
if (lookupClassifier != null) {
return lookupClassifier;
}
}
}
EClassifier result = super.lookupClassifier(names);
return (result != null) ? result : QvtOperationalStdLibrary.INSTANCE.lookupClassifier(names);
}
@Override
public EPackage lookupPackage(List<String> path) {
if (path.size() > 1) {
// Qualified type
// It is possible to either qualify the type name with a model type or a package name
if (myModelTypeRegistry.containsKey(path.get(0))) {
EPackage lookupPackage = doLookupModeltypePackage(
myModelTypeRegistry.get(path.get(0)), path.subList(1, path.size()));
if (lookupPackage != null) {
return lookupPackage;
}
}
} else if(path.size() == 1) {
EPackage stdPackage = QvtOperationalStdLibrary.INSTANCE.getStdLibModule();
if(stdPackage.getName().equals(path.get(0))) {
return stdPackage;
}
}
return super.lookupPackage(path);
}
private EPackage doLookupModeltypePackage(ModelType modelType, List<String> path) {
return lookupPackageableElement(modelType, path, LOOKUP_PACKAGE_DELEGATE);
}
private EClassifier doLookupModeltypeClassifier(ModelType modelType, List<String> path) {
return lookupPackageableElement(modelType, path, LOOKUP_CLASSIFIER_DELEGATE);
}
private <T extends EObject> T lookupPackageableElement(ModelType modelType, List<String> path, LookupPackageableElementDelegate<T> lookupPackageableElementDelegate) {
EPackage oldContext = super.getContextPackage();
T result = null;
List<EPackage> metamodels = modelType.getMetamodel();
for (EPackage pkg : metamodels) {
super.setContextPackage(pkg);
result = lookupPackageableElementDelegate.lookupPackageableElement(path);
EObject eContainer = result;
for (int i = 0, n = path.size(); i < n; ++i) {
if (eContainer == null) {
break;
}
eContainer = eContainer.eContainer();
}
if (eContainer != pkg) {
result = null;
}
if (result != null) {
break;
}
}
super.setContextPackage(oldContext);
return result;
}
@Override
public EOperation defineOperation(EClassifier owner, String name, EClassifier type,
List<org.eclipse.ocl.expressions.Variable<EClassifier, EParameter>> params,
Constraint constraint) {
EOperation result = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEOperation();
result.setName(name);
result.setEType((type == null) ? getOCLStandardLibrary().getOclVoid() : type);
for (Variable<EClassifier, EParameter> next : params) {
EParameter param = org.eclipse.emf.ecore.EcoreFactory.eINSTANCE.createEParameter();
param.setName(next.getName());
param.setEType((next.getType() == null)? getOCLStandardLibrary().getOclVoid() : next.getType());
result.getEParameters().add(param);
}
if(owner == getModuleContextType()) {
getModuleContextType().getEOperations().add(result);
} else {
addHelperOperation(owner, result);
}
return result;
}
public ImperativeOperation defineImperativeOperation(ImperativeOperation operation, boolean isMappingOperation,
boolean isCheckDuplicates) {
EClassifier ownerType = QvtOperationalParserUtil.getContextualType(operation);
boolean isContextual = true;
if (ownerType == null) {
isContextual = false;
ownerType = getModuleContextType();
}
boolean isError = false;
ImperativeOperation newOperation = operation;
CollisionStatus collidingOperStatus = findCollidingOperation(ownerType, newOperation);
if(collidingOperStatus != null) {
EOperation collidingOper = collidingOperStatus.getOperation();
if(collidingOperStatus.getCollisionKind() == CollisionKind.ALREADY_DEFINED) {
isError = true;
HiddenElementAdapter.markAsHidden(operation);
reportError(NLS.bind(ValidationMessages.SemanticUtil_0, new Object[] {
operation.getName(), ownerType.getName() }),
operation.getStartPosition(), operation.getEndPosition());
}
else if(collidingOperStatus.getCollisionKind() == CollisionKind.OVERRIDES) {
if(collidingOper instanceof ImperativeOperation) {
// only imperative operations can be overridden
EClassifier overriddenReturnType = collidingOper.getEType();
EClassifier newReturnType = newOperation.getEType();
if(newReturnType != null && overriddenReturnType != null) {
if(TypeUtil.compatibleTypeMatch(this, newReturnType, overriddenReturnType)) {
newOperation.setOverridden((ImperativeOperation)collidingOper);
} else {
isError = true;
reportError(NLS.bind(ValidationMessages.OperationOverrideWithInvalidReturnType, new Object[] {
getUMLReflection().getName(operation),
getUMLReflection().getName(collidingOper.getEContainingClass()) }),
operation.getStartPosition(), operation.getEndPosition());
}
}
}
if(!isError) {
Module owningModule = QvtOperationalParserUtil.getOwningModule(collidingOper);
if(owningModule == null || owningModule == getQVTStandardLibrary().getStdLibModule()) {
reportWarning(NLS.bind(ValidationMessages.HidingStdlibOperationDiscouraged, operation.getName()),
operation.getStartPosition(), operation.getEndPosition());
}
}
}
else if(collidingOperStatus.getCollisionKind() == CollisionKind.VIRTUAL_METHOD_SUBTYPE) {
isError = true;
HiddenElementAdapter.markAsHidden(operation);
reportError(NLS.bind(ValidationMessages.ReturnSubtypeMismatch,
operation.getName(), QvtOperationalTypesUtil.getTypeFullName(collidingOperStatus.getOperation().getEType())),
operation.getStartPosition(), operation.getEndPosition());
}
else if(collidingOperStatus.getCollisionKind() == CollisionKind.VIRTUAL_METHOD_SUPERTYPE) {
isError = true;
HiddenElementAdapter.markAsHidden(operation);
reportError(NLS.bind(ValidationMessages.ReturnSupertypeMismatch,
operation.getName(), QvtOperationalTypesUtil.getTypeFullName(collidingOperStatus.getOperation().getEType())),
operation.getStartPosition(), operation.getEndPosition());
}
else {
assert false;
}
}
if(isContextual && !isError) {
getTypeResolver().resolveAdditionalOperation(ownerType, newOperation);
}
getModuleContextType().getEOperations().add(newOperation);
return newOperation;
}
@Override
public Module getModuleContextType() {
return getInternalParent() instanceof QvtOperationalEnv ? ((QvtOperationalEnv)getInternalParent()).getModuleContextType() : null;
}
private void defineParameterVar(EParameter parameter) {
Variable<EClassifier, EParameter> var;
if(parameter instanceof VarParameter) {
var = (VarParameter) parameter;
} else {
var = EcoreFactory.eINSTANCE.createVariable();
var.setName(parameter.getName());
var.setType(parameter.getEType());
var.setRepresentedParameter(parameter);
}
addElement(parameter.getName(), var, true);
}
private void defineOperationParameters(EOperation operation) {
final ImperativeOperation imperativeOperation = (operation instanceof ImperativeOperation) ? (ImperativeOperation) operation : null;
final boolean isMapping = operation instanceof MappingOperation;
final boolean hasMultipleResultParams = (imperativeOperation != null) ? imperativeOperation.getResult().size() > 1 : false;
if(imperativeOperation != null && QvtOperationalParserUtil.isContextual(imperativeOperation)) {
VarParameter context = imperativeOperation.getContext();
assert context != null;
addElement(context.getName(), context, false);
}
for (EParameter parameter : operation.getEParameters()) {
defineParameterVar(parameter);
}
if(isMapping || hasMultipleResultParams) {
assert imperativeOperation != null;
for (VarParameter parameter : imperativeOperation.getResult()) {
defineParameterVar(parameter);
}
}
}
public void setQvtCompilerOptions(QvtCompilerOptions options) {
myCompilerOptions = options;
}
public void setCheckForDuplicateErrors(boolean checkForDuplicateErrors) {
myCheckForDuplicateErrors = checkForDuplicateErrors;
}
public void registerMappingOperation(MappingOperation operation) {
if (getInternalParent() != null) {
((QvtOperationalEnv) getInternalParent()).registerMappingOperation(operation);
} else {
EClassifier ownerType = QvtOperationalParserUtil.getContextualType(operation);
if (ownerType == null) {
ownerType = getUMLReflection().getOwningClassifier(operation);
if(ownerType == null) {
return;
}
}
MappingsMapKey key = new MappingsMapKey(ownerType, operation.getName());
List<MappingOperation> sameNameAndContextOperations = myMappingsMap.get(key);
if (sameNameAndContextOperations == null) {
sameNameAndContextOperations = new ArrayList<MappingOperation>();
myMappingsMap.put(key, sameNameAndContextOperations);
}
sameNameAndContextOperations.add(operation);
}
}
public void registerResolveInExp(ResolveInExp resolveInExp, EClassifier referredMappingContextType, String mappingName) {
if (getInternalParent() != null) {
((QvtOperationalEnv) getInternalParent()).registerResolveInExp(resolveInExp, referredMappingContextType, mappingName);
} else {
myResolveInExps.put(resolveInExp, new MappingsMapKey(referredMappingContextType, mappingName));
}
}
public void resolveResolveInExpInMappings() {
if (getInternalParent() == null) {
for (Map.Entry<ResolveInExp, MappingsMapKey> entry : myResolveInExps.entrySet()) {
MappingsMapKey mappingsMapKey = entry.getValue();
List<MappingOperation> sameNameAndContextOperations = myMappingsMap.get(mappingsMapKey);
ResolveInExp resolveInExp = entry.getKey();
if (sameNameAndContextOperations != null) {
for (MappingOperation mappingOperation : sameNameAndContextOperations) {
if(resolveInExp.getInMapping() == null) {
// Keep only the first occurence found
resolveInExp.setInMapping(mappingOperation);
}
}
}
// assert inMapping has been set, otherwise parse error should have been reported
assert resolveInExp.getInMapping() != null;
}
} else {
((QvtOperationalEnv)getInternalParent()).resolveResolveInExpInMappings();
}
}
@Override
protected ProblemHandler createDefaultProblemHandler(AbstractParser parser) {
return new AbstractProblemHandler(parser) {
@Override
public void handleProblem(Severity problemSeverity, Phase processingPhase, String problemMessage,
String processingContext, int startOffset, int endOffset) {
boolean allowCsUnboundValidationProblems = false;
if(isMDTOCLCompatibilityFalseProblem(allowCsUnboundValidationProblems, problemSeverity,
processingPhase, problemMessage, processingContext, startOffset, endOffset)) {
// Remark: Not a real problem we can handle now, but we keep in mind ;)
return;
}
if(problemSeverity == Severity.INFO || problemSeverity == Severity.OK || problemSeverity == Severity.WARNING) {
reportWarning(problemMessage, startOffset, endOffset);
} else {
reportError(problemMessage, startOffset, endOffset);
}
}
};
}
@Override
public void analyzerError(String problemMessage, String problemContext, Object problemObject) {
CSTNode cstNode = getASTMapping(problemObject);
int startOffset = cstNode != null ? cstNode.getStartOffset() : -1;
int endOffset = cstNode != null ? cstNode.getEndOffset() : -1;
if(cstNode == null && problemObject instanceof ASTNode) {
ASTNode astNode = (ASTNode) problemObject;
startOffset = astNode.getStartPosition();
endOffset = astNode .getEndPosition();
}
analyzerError(problemMessage, problemContext, startOffset, endOffset);
}
@Override
public void initASTMapping(Object astNode, CSTNode cstNode) {
if(astNode instanceof ASTNode) {
ASTNode castNode = (ASTNode) astNode;
if(castNode.getEndPosition() < 0) {
castNode.setStartPosition(cstNode.getStartOffset());
castNode.setEndPosition(cstNode.getEndOffset());
}
}
super.initASTMapping(astNode, cstNode);
}
@Override
public void close() {
setParser(null);
setProblemHandler(null);
setASTNodeToCSTNodeMap(null);
super.close();
}
private static int getLineNum(QvtOperationalEnv env, int startOffset) {
if(startOffset < 0) {
return -1;
}
AbstractParser parser = env.getParser();
if(parser != null) {
AbstractLexer lexer = parser.getLexer();
if(lexer != null) {
if(startOffset <= lexer.getILexStream().getStreamLength()) {
try {
return lexer.getILexStream().getLineNumberOfCharAt(startOffset);
} catch (RuntimeException e) {
// TODO - add trace
// do nothing, the line number just not available
}
}
}
}
return -1;
}
private static EPackage.Registry createDefaultPackageRegistry() {
return new EPackageRegistryImpl();
}
/**
* This operation indicates whether the given problem is a real QVT domain
* problem or a compatibility with MDT OCL to solve, thus not a problem to be propagated.
* For example, AST validation problem with not source text location, error conditions
* that are valid in QVT, etc.
* <p>
* @param allowCsUnboundValidationProblems <code>false</code> indicates that all OCL AST
* validation problems without CST binding will be recognized as a false compatibility problem
*/
public static boolean isMDTOCLCompatibilityFalseProblem(
boolean allowCsUnboundValidationProblems,
Severity problemSeverity, Phase processingPhase, String problemMessage,
String processingContext, int startOffset, int endOffset) {
// FIXME - filter out those MDT OCL validation problems we can not handle yet
if (Phase.VALIDATOR == processingPhase && startOffset == -1 && endOffset == -1
&& !allowCsUnboundValidationProblems) {
return true;
}
// FIXME - workaround until fixed [https://bugs.eclipse.org/bugs/show_bug.cgi?id=244144]
if("collectionTypeResultTypeOf".equals(processingContext)) { //$NON-NLS-1$
return true;
}
return false;
}
private interface LookupPackageableElementDelegate<T> {
public T lookupPackageableElement(List<String> names);
};
}