/*******************************************************************************
* Copyright (c) 2007, 2015 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 - bug 475123
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.ast.env;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
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.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalUtil;
import org.eclipse.m2m.internal.qvt.oml.evaluator.ThisInstanceResolver;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.SendSignalAction;
/**
* Virtual table class grouping related virtual operations to the table owning operation.
* @since 2.0
*/
public abstract class VirtualTable implements IVirtualOperationTable {
private List<EOperation> fOperations;
protected VirtualTable() {
}
protected abstract EOperation getOwningOperation();
/**
* Adds operation that is virtual in relation to the owning operation of this table.
* <p>
* Note: No check is done for non-compatible signature, owner type. It supposed to be done
* done externally.
*
* @param operation
* operation to add to this table
*/
public void addOperation(EOperation operation) {
if(operation == null) {
throw new IllegalArgumentException();
}
if(fOperations == null) {
fOperations = new LinkedList<EOperation>();
}
if(!fOperations.contains(operation)) {
fOperations.add(operation);
}
}
public EOperation lookupActualOperation(
EClassifier actualContextType,
Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env,
InternalEvaluationEnv evalEnv) {
return lookupActualOperation(actualContextType, env, null, evalEnv);
}
public EOperation lookupActualOperation(
EClassifier actualContextType,
Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env,
Module scope, InternalEvaluationEnv evalEnv) {
if(actualContextType == null || env == null) {
throw new IllegalArgumentException();
}
EOperation formalOperation = this.getOwningOperation();
if(formalOperation == null) {
return null;
}
if(env.getUMLReflection().getOwningClassifier(formalOperation) == actualContextType) {
// the closest match satisfied
return formalOperation;
}
Collection<EOperation> candidateOperations = QvtOperationalUtil.filterOverriddenOperations(getOperations());
for (EOperation nextOperation : candidateOperations) {
if(env.getUMLReflection().getOwningClassifier(nextOperation) == actualContextType) {
if(isOperationInScope(nextOperation, evalEnv)) {
return nextOperation;
}
}
}
if(actualContextType instanceof EClass) {
// try lookup in actual type's super-types for the closest match
for (EClass superClass : ((EClass)actualContextType).getESuperTypes()) {
EOperation superOperation = lookupActualOperation(superClass, env, scope, evalEnv);
if(superOperation != null) {
return superOperation;
}
}
}
return null;
}
public Collection<EOperation> getOperations() {
return fOperations != null ? fOperations : Collections.<EOperation>emptySet();
}
private boolean isOperationInScope(EOperation operation, InternalEvaluationEnv evalEnv) {
if(evalEnv != null && operation != null) {
Module module = QvtOperationalParserUtil.getOwningModule(operation);
if(module != null) {
ThisInstanceResolver thisResolver = evalEnv.getThisResolver();
return thisResolver != null && thisResolver.getThisInstanceOf(module) != null;
} else {
// coming from metamodel or the OCL Standard Library
// call-able in any scope
return true;
}
}
return false;
}
}