blob: 146318b928ee46f1232043f21c2aba781ba88525 [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.traceback;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.PropertyCallExp;
import org.eclipse.ocl.ecore.TupleType;
import org.eclipse.ocl.examples.impactanalyzer.impl.OperationBodyToCallMapper;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.unusedEvaluation.UnusedEvaluationRequestFactory;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.unusedEvaluation.UnusedEvaluationRequestSet;
import org.eclipse.ocl.examples.impactanalyzer.util.AnnotatedEObject;
import org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory;
import org.eclipse.ocl.examples.impactanalyzer.util.OperationCallExpKeyedSet;
public class PropertyCallTracebackStep extends AbstractTracebackStep<PropertyCallExp> {
private enum Strategy { TUPLE, OPPOSITE_MANY, OPPOSITE_SINGLE, CONTAINMENT, HIDDEN_OPPOSITE };
private final TracebackStepAndScopeChange nextStep;
private final PropertyCallExp sourceExpression;
private final EReference oppositeReference;
private final Strategy strategy;
public PropertyCallTracebackStep(PropertyCallExp sourceExpression, EClass context,
OperationBodyToCallMapper operationBodyToCallMapper, Stack<String> tupleLiteralNamesToLookFor,
TracebackStepCache tracebackStepCache, UnusedEvaluationRequestFactory unusedEvaluationRequestFactory, OCLFactory oclFactory) {
super(sourceExpression, tupleLiteralNamesToLookFor, tracebackStepCache.getOppositeEndFinder(), operationBodyToCallMapper, unusedEvaluationRequestFactory, oclFactory);
this.sourceExpression = sourceExpression;
OCLExpression source = (OCLExpression) sourceExpression.getSource();
Stack<String> tupleLiteralNames;
if (source.getType() instanceof TupleType) {
if (tupleLiteralNamesToLookFor == null) {
tupleLiteralNames = new Stack<String>();
} else {
tupleLiteralNames = cloneWithTypeCheck(tupleLiteralNamesToLookFor);
}
tupleLiteralNames.push(sourceExpression.getReferredProperty().getName());
} else {
tupleLiteralNames = tupleLiteralNamesToLookFor;
}
if (sourceExpression.getReferredProperty() instanceof EReference) {
oppositeReference = ((EReference) sourceExpression.getReferredProperty()).getEOpposite();
} else {
oppositeReference = null;
}
nextStep = createTracebackStepAndScopeChange(sourceExpression, source, context, operationBodyToCallMapper,
tupleLiteralNames, tracebackStepCache);
strategy = determineStrategy();
}
private Strategy determineStrategy() {
Strategy result;
EStructuralFeature feature = sourceExpression.getReferredProperty();
if (sourceExpression.getSource().getType() instanceof TupleType) {
result = Strategy.TUPLE;
} else if (feature instanceof EAttribute){
// From the comment of EAttribute:
// "The {@link #getEType() type} of an attribute must always be a data type; this method provides access to it."
// Together with the validation rules for EAttributes this ensures that attributes only have data types
// as their types. Therefore, they can never have EObjects as their values.
throw new IllegalArgumentException("An EAttribute should never be reached while traceback.");
} else if (feature instanceof EReference) {
EReference ref = (EReference) feature;
if (ref.getEOpposite() != null){
if (ref.getEOpposite().isMany()){
result = Strategy.OPPOSITE_MANY;
} else {
result = Strategy.OPPOSITE_SINGLE;
}
} else if (ref.isContainment()) {
result = Strategy.CONTAINMENT;
} else {
result = Strategy.HIDDEN_OPPOSITE;
}
} else {
throw new RuntimeException("Don't know what type of feature this is: "+feature);
}
return result;
}
@Override
protected OperationCallExpKeyedSet performSubsequentTraceback(AnnotatedEObject source,
UnusedEvaluationRequestSet pendingUnusedEvalRequests, org.eclipse.ocl.examples.impactanalyzer.instanceScope.traceback.TracebackCache tracebackCache, Notification changeEvent) {
OperationCallExpKeyedSet result = tracebackCache.getOperationCallExpKeyedSetFactory().emptySet();
switch (strategy) {
case TUPLE:
result = nextStep.traceback(annotateEObject(source), pendingUnusedEvalRequests, tracebackCache, changeEvent);
break;
case OPPOSITE_MANY:
Object o = source.eGet(oppositeReference);
if (o instanceof EList<?>) {
@SuppressWarnings("unchecked")
EList<EObject> refObjects = (EList<EObject>) o;
Set<OperationCallExpKeyedSet> resultSets = new HashSet<OperationCallExpKeyedSet>();
for (EObject obj : refObjects) {
resultSets.add(nextStep.traceback(annotateEObject(source, obj), pendingUnusedEvalRequests, tracebackCache, changeEvent));
}
result = tracebackCache.getOperationCallExpKeyedSetFactory().createOperationCallExpKeyedSet(resultSets);
}
break;
case OPPOSITE_SINGLE:
EObject oSingle = (EObject) source.eGet(oppositeReference);
if (oSingle != null) {
result = nextStep.traceback(annotateEObject(source, oSingle), pendingUnusedEvalRequests, tracebackCache, changeEvent);
}
break;
case CONTAINMENT:
EObject container = source.eContainer();
if (container != null) {
result = nextStep.traceback(annotateEObject(source, container), pendingUnusedEvalRequests, tracebackCache, changeEvent);
}
break;
case HIDDEN_OPPOSITE:
Collection<EObject> opposite = getOppositeEndFinder().navigateOppositePropertyWithBackwardScope(
(EReference) sourceExpression.getReferredProperty(), source.getAnnotatedObject());
if (opposite != null && !opposite.isEmpty()) {
Set<OperationCallExpKeyedSet> resultSets = new HashSet<OperationCallExpKeyedSet>();
for (EObject eo : opposite) {
resultSets.add(nextStep.traceback(annotateEObject(source, eo), pendingUnusedEvalRequests, tracebackCache, changeEvent));
}
result = tracebackCache.getOperationCallExpKeyedSetFactory().createOperationCallExpKeyedSet(resultSets);
}
break;
default:
throw new RuntimeException("Unknown property call traceback strategy "+strategy);
}
return result;
}
}