blob: 7e94c67dcbb9af6b61098552cf39894e79f2dd89 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* SAP AG - initial API and implementation
******************************************************************************/
package org.eclipse.ocl.examples.impactanalyzer.instanceScope;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.OperationCallExp;
import org.eclipse.ocl.ecore.TypeExp;
import org.eclipse.ocl.ecore.Variable;
import org.eclipse.ocl.ecore.impl.TypeExpImpl;
import org.eclipse.ocl.examples.impactanalyzer.impl.OperationBodyToCallMapper;
import org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory;
import org.eclipse.ocl.utilities.PredefinedType;
public class OperationCallExpTracer extends AbstractTracer<OperationCallExp> {
private static final Set<String> sourcePassThroughStdLibOpNames;
private static final Set<String> argumentPassThroughStdLibOpNames;
static {
sourcePassThroughStdLibOpNames = new HashSet<String>();
sourcePassThroughStdLibOpNames.add(PredefinedType.ANY_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.AS_BAG_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.AS_SET_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.AS_ORDERED_SET_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.AS_SEQUENCE_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.AT_NAME);
// sourcePassThroughStdLibOpNames.add(PredefinedType.ATRPRE_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.EXCLUDING_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.FIRST_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.FLATTEN_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.INCLUDING_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.INSERT_AT_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.APPEND_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.INTERSECTION_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.OCL_AS_TYPE_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.UNION_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.SELECT_BY_KIND_NAME);
sourcePassThroughStdLibOpNames.add(PredefinedType.SELECT_BY_TYPE_NAME);
argumentPassThroughStdLibOpNames = new HashSet<String>();
argumentPassThroughStdLibOpNames.add(PredefinedType.INCLUDING_NAME);
argumentPassThroughStdLibOpNames.add(PredefinedType.INSERT_AT_NAME);
argumentPassThroughStdLibOpNames.add(PredefinedType.APPEND_NAME);
argumentPassThroughStdLibOpNames.add(PredefinedType.UNION_NAME);
// TODO what about "product"?
}
public OperationCallExpTracer(OperationCallExp expression, Stack<String> tuplePartNames, OCLFactory oclFactory) {
super(expression, tuplePartNames, oclFactory);
}
@Override
public NavigationStep traceback(EClass context, PathCache pathCache, OperationBodyToCallMapper operationBodyToCallMapper) {
NavigationStep result;
OCLExpression body = operationBodyToCallMapper.getOperationBody(getExpression().getReferredOperation());
if (body != null) {
// the operation body may lead to a recursion; to avoid a recursion we first create an
// indirecting step here and insert it into the path cache so it will be found instead
// of recurring
IndirectingStep bodyStep = pathCache.createIndirectingStepFor(getExpression(), getTupleLiteralPartNamesToLookFor());
// an OCL-specified operation; trace back using the body expression
NavigationStep actualStep = pathCache.getOrCreateNavigationPath(body, context, operationBodyToCallMapper, getTupleLiteralPartNamesToLookFor(), oclFactory);
bodyStep.setActualStep(actualStep);
result = bodyStep;
} else {
String opName = getExpression().getReferredOperation().getName();
if (opName.equals(PredefinedType.OCL_AS_TYPE_NAME)) {
OCLExpression argument = (OCLExpression) (getExpression().getArgument()).get(0);
if (argument instanceof TypeExp) {
EClassifier type = ((TypeExpImpl) argument).getReferredType();
IdentityNavigationStep identityStep = new IdentityNavigationStep((EClass) getExpression().getType(), (EClass) type,
getExpression());
NavigationStep sourceStep = pathCache.getOrCreateNavigationPath((OCLExpression) getExpression().getSource(),
context, operationBodyToCallMapper, getTupleLiteralPartNamesToLookFor(), oclFactory);
result = pathCache.navigationStepFromSequence(getExpression(), getTupleLiteralPartNamesToLookFor(), identityStep, sourceStep);
} else {
throw new RuntimeException("What else could be the argument of oclAsType if not a TypeExp? "
+ (argument.eClass()).getName());
}
} else if (sourcePassThroughStdLibOpNames.contains(opName)) {
// FIXME handle product
NavigationStep sourcePath = pathCache.getOrCreateNavigationPath((OCLExpression) getExpression()
.getSource(), context, operationBodyToCallMapper, getTupleLiteralPartNamesToLookFor(), oclFactory);
if (argumentPassThroughStdLibOpNames.contains(opName)) {
int paramPos = 0;
if (opName.equals(PredefinedType.INSERT_AT_NAME)) {
// "insertAt" takes two arguments, the index and the object to add.
// The OCL spec says the index comes first, so getting the first argument makes no sense in this case.
paramPos = 1;
}
OCLExpression argument = (OCLExpression) (getExpression().getArgument()).get(paramPos);
NavigationStep argumentPath = pathCache.getOrCreateNavigationPath(argument, context, operationBodyToCallMapper, getTupleLiteralPartNamesToLookFor(), oclFactory);
result = pathCache.navigationStepForBranch(
getInnermostElementType(getExpression().getType()),
context,
getExpression(),
getTupleLiteralPartNamesToLookFor(),
/* requireExactMatchForSourceType */ opName.equals(PredefinedType.SELECT_BY_TYPE_NAME),
sourcePath, argumentPath);
} else {
result = pathCache.navigationStepForBranch(
getInnermostElementType(getExpression().getType()),
context,
getExpression(),
getTupleLiteralPartNamesToLookFor(),
/* requireExactMatchForSourceType */ opName.equals(PredefinedType.SELECT_BY_TYPE_NAME),
sourcePath);
}
} else if (opName.equals(PredefinedType.ALL_INSTANCES_NAME)) {
// the object from where to trace back later in the navigate method may not
// conform to the type on which allInstances() is invoked here; for example, the
// expression may navigate from the result of allInstances() across an association
// defined on a superclass of the one on which allInstances() was invoked. Therefore,
// ensure that the typing of the AllInstancesNavigationStep is correct.
EClass classifier = (EClass) ((TypeExp) getExpression().getSource())
.getReferredType();
result = new AllInstancesNavigationStep(classifier, context, getExpression(), pathCache.getOppositeEndFinder()); // non-absolute
} else {
result = new EmptyResultNavigationStep(getExpression());
// hope, we didn't forget stdlib operations that pass on
// source or argument values into their result
}
}
applyScopesOnNavigationStep(result, operationBodyToCallMapper);
return result;
}
@Override
protected Set<Variable> calculateEnteringScope(OperationBodyToCallMapper operationBodyToCallMapper) {
OCLExpression body = operationBodyToCallMapper.getOperationBody(getExpression().getReferredOperation());
if (body != null){
// an OCL-specified operation, the body creates a new scope
return getVariablesScopedByExpression(body, operationBodyToCallMapper);
}
// standard OCL operations do not alter the scope as we cannot trace into their implementation
return Collections.emptySet();
}
/**
* When tracing into the called operation's body, all variables currently in scope are left.
*/
@Override
protected Set<Variable> calculateLeavingScopes(OperationBodyToCallMapper operationBodyToCallMapper) {
return getAllVariablesInScope(getExpression(), operationBodyToCallMapper);
}
}