blob: 2085b5a21ec872756d67d9450420450e907d0d09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Willink Transformations 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:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvtp2qvts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.NullLiteralExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.OperationCallExp;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.ids.OperationId;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.qvtd.pivot.qvtbase.Domain;
import org.eclipse.qvtd.pivot.qvtbase.Predicate;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtcorebase.AbstractMapping;
import org.eclipse.qvtd.pivot.qvtcorebase.Assignment;
import org.eclipse.qvtd.pivot.qvtcorebase.BottomPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain;
import org.eclipse.qvtd.pivot.qvtcorebase.CorePattern;
import org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern;
import org.eclipse.qvtd.pivot.qvtcorebase.NavigationAssignment;
import org.eclipse.qvtd.pivot.qvtcorebase.RealizedVariable;
import org.eclipse.qvtd.pivot.qvtcorebase.utilities.QVTcoreBaseUtil;
import org.eclipse.qvtd.pivot.schedule.AbstractAction;
import org.eclipse.qvtd.pivot.schedule.ClassDatum;
import org.eclipse.qvtd.pivot.schedule.MappingAction;
/**
* A BasicMappingRegion provides the initial QVTs node-edge graph representation of a QVTp mapping.
*/
public class BasicMappingRegion extends AbstractMappingRegion
{
public static @NonNull BasicMappingRegion createMappingRegion(@NonNull MultiRegion multiRegion, @NonNull MappingAction mappingAction) {
BasicMappingRegion mappingRegion = new BasicMappingRegion(multiRegion, mappingAction);
mappingRegion.initialize();
return mappingRegion;
}
/**
* The analyzed action.
*/
private final @NonNull MappingAction mappingAction;
/**
* Predicates that are too complex to analyze. i.e. more than a comparison of a bound variable wrt
* a property call chain on another bound variable.
*/
private final @NonNull Set<@NonNull Predicate> complexPredicates = new HashSet<>();
private final @NonNull ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer(this);
/**
* All the guardpatterns. (domain and mapping).
*/
private final @NonNull List<@NonNull GuardPattern> guardPatterns = new ArrayList<>();
/**
* All the bottom patterns. (domain and mapping).
*/
private final @NonNull List<@NonNull BottomPattern> bottomPatterns = new ArrayList<>();
/**
* The node for each navigable VariableDeclaration.
*/
private final @NonNull List<@NonNull NavigationAssignment> navigationAssignments = new ArrayList<>();
/**
* The node for each navigable VariableDeclaration.
*/
private final @NonNull Map<@NonNull VariableDeclaration, @NonNull Node> variable2node = new HashMap<>();
/**
* The extra guards to accommodate operation content.
*/
private /*@LazyNonNull*/ List<@NonNull Node> extraNodes = null;
private BasicMappingRegion(@NonNull MultiRegion multiRegion, @NonNull MappingAction mappingAction) {
super(multiRegion);
this.mappingAction = mappingAction;
AbstractMapping mapping = mappingAction.getMapping();
assert mapping != null;
//
guardPatterns.add(ClassUtil.nonNull(mapping.getGuardPattern()));
bottomPatterns.add(ClassUtil.nonNull(mapping.getBottomPattern()));
for (Domain domain : mapping.getDomain()) {
if (domain instanceof CoreDomain) {
CoreDomain coreDomain = (CoreDomain)domain;
//
GuardPattern guardPattern = ClassUtil.nonNull(coreDomain.getGuardPattern());
BottomPattern bottomPattern = ClassUtil.nonNull(coreDomain.getBottomPattern());
guardPatterns.add(guardPattern);
bottomPatterns.add(bottomPattern);
for (@NonNull Assignment assignment : ClassUtil.nullFree(bottomPattern.getAssignment())) {
if (assignment instanceof NavigationAssignment) {
navigationAssignments.add((NavigationAssignment)assignment);
}
}
}
}
}
public void addVariableNode(@NonNull VariableDeclaration typedElement, @NonNull Node simpleNode) {
// assert !simpleNode.isOperation(); // FIXME testExample2_V2 violates this for an intermediate "if"
variable2node.put(typedElement, simpleNode);
}
/**
* Create the BLUE/CYAN computations for the RHS of assignments.
*/
protected void analyzeAssignmentValues() {
AssignmentSorter assignmentSorter = new AssignmentSorter();
for (@NonNull BottomPattern bottomPattern : bottomPatterns) {
assignmentSorter.addAll(ClassUtil.nullFree(bottomPattern.getAssignment()));
}
for (@NonNull Assignment assignment : assignmentSorter.getSortedAssignments()) {
assignment.accept(expressionAnalyzer);
}
}
protected void analyzeComplexPredicates() {
/**
* Identify any assignments and hidden inputs.
*/
for (Predicate predicate : complexPredicates) {
OCLExpression conditionExpression = predicate.getConditionExpression();
/* if (conditionExpression instanceof OperationCallExp) {
OperationCallExp callExp = (OperationCallExp)conditionExpression;
OperationId operationId = callExp.getReferredOperation().getOperationId();
if (SchedulerConstants.isSameOperation(operationId, getSchedulerConstants().getOclAnyEqualsId())) {
OCLExpression leftExpression = callExp.getOwnedSource();
OCLExpression rightExpression = callExp.getOwnedArguments().get(0);
Node leftNode = expressionAnalyzer.analyze(leftExpression);
Node rightNode = expressionAnalyzer.analyze(rightExpression);
if (leftNode != rightNode) {
if (leftNode.isKnown() && !(leftExpression instanceof NavigationCallExp)) {
Edges.ARGUMENT.createEdge(this, leftNode, "=", rightNode);
}
else if (rightNode.isKnown() && !(rightExpression instanceof NavigationCallExp)) {
Edges.ARGUMENT.createEdge(this, rightNode, "=", leftNode);
}
else if (leftNode.isKnown()) {
Edges.ARGUMENT.createEdge(this, leftNode, "=", rightNode);
}
else if (rightNode.isKnown()) {
Edges.ARGUMENT.createEdge(this, rightNode, "=", leftNode);
}
else {
Edges.BINDING.createEdge(this, leftNode, null, rightNode); // FIXME
Edges.BINDING.createEdge(this, rightNode, null, leftNode);
}
}
}
}
else { */
Node resultNode = expressionAnalyzer.analyze(conditionExpression);
if (!resultNode.isTrue()) {
Node trueNode = Nodes.createTrueNode(this);
Edges.createPredicateEdge(resultNode, null, trueNode);
}
else { // FIXME ?? do includes() here explicitly
resultNode.destroy();
}
// }
}
}
/**
* Create a BLUE/CYAN node for each guard variable.
*/
protected void analyzeGuardVariables() {
for (@NonNull GuardPattern guardPattern : guardPatterns) {
for (@NonNull Variable guardVariable : ClassUtil.nullFree(guardPattern.getVariable())) {
Node guardNode = getNode(guardVariable);
assert guardNode == null;
guardNode = Nodes.createGuardNode(this, guardVariable);
assert guardNode == getNode(guardVariable);
}
}
}
/**
* Analyze the initializers to form predicates / computations.
*/
protected void analyzeInitializers(@NonNull Iterable<@NonNull ? extends CorePattern> corePatterns) {
for (@NonNull CorePattern corePattern : corePatterns) {
for (@NonNull Variable variable : ClassUtil.nullFree(corePattern.getVariable())) {
OCLExpression ownedInit = variable.getOwnedInit();
if (ownedInit != null) {
analyzeVariable(variable, ownedInit);
}
}
if (corePattern instanceof BottomPattern) {
for (@NonNull RealizedVariable realizedVariable : ClassUtil.nullFree(((BottomPattern)corePattern).getRealizedVariable())) {
OCLExpression ownedInit = realizedVariable.getOwnedInit();
if (ownedInit != null) {
analyzeVariable(realizedVariable, ownedInit);
}
}
}
}
}
/**
* Analyze the predicates to partition the guard variables into the distinct inputs that are not mutually
* navigable as a consequence of predicate constraints.
* @param bottomPatterns
*/
protected void analyzePredicates(@NonNull List<@NonNull ? extends CorePattern> corePatterns) {
//
// Populate the targetVariable2sourceVariable2paths from the simple "a.b = c" style predicates,
// and cache those that are too hard to analyze as complex predicates.
//
for (@NonNull CorePattern corePattern : corePatterns) {
for (@NonNull Predicate predicate : ClassUtil.nullFree(corePattern.getPredicate())) {
OCLExpression conditionExpression = predicate.getConditionExpression();
if (conditionExpression != null) {
OCLExpression boundExpression = getPredicateComparisonBoundExpression(conditionExpression);
if (boundExpression instanceof VariableExp) {
OCLExpression referenceExpression = getPredicateComparisonReferenceExpression(conditionExpression);
assert referenceExpression != null;
analyzeSimplePredicate(((VariableExp)boundExpression).getReferredVariable(), referenceExpression);
}
else if (boundExpression instanceof NullLiteralExp) {
OCLExpression referenceExpression = getPredicateComparisonReferenceExpression(conditionExpression);
assert referenceExpression != null;
analyzeSimplePredicate(null, referenceExpression);
}
else {
complexPredicates.add(predicate);
}
}
}
}
return;
}
/**
* Create a GREEN node for each realized variable.
*/
protected void analyzeRealizedVariables() {
for (@NonNull BottomPattern bottomPattern : bottomPatterns) {
for (@NonNull RealizedVariable realizedVariable : ClassUtil.nullFree(bottomPattern.getRealizedVariable())) {
Node realizedNode = getNode(realizedVariable);
assert realizedNode == null;
realizedNode = Nodes.createRealizedStepNode(this, realizedVariable);
assert realizedNode == getNode(realizedVariable);
}
}
}
//
// Install the targetVariable2sourceVariable2paths entries for a "boundVariable = referenceExpression" predicate,
// where the referenceExpression involves zero or more PropertyCallExps of a VariableExp. boundVariable may be null
// for a negative application condition.
//
// A reverse entry is also created if no PropertyCallExp is not to-one.
//
private void analyzeSimplePredicate(@Nullable VariableDeclaration boundVariable, @NonNull OCLExpression referenceExpression) {
for (@NonNull OCLExpression expression = referenceExpression; expression instanceof NavigationCallExp; ) {
NavigationCallExp navigationCallExp = (NavigationCallExp)expression;
Property referredProperty = PivotUtil.getReferredProperty(navigationCallExp);
assert referredProperty != null;
expression = ClassUtil.nonNullState(navigationCallExp.getOwnedSource());
if (expression instanceof VariableExp) {
VariableDeclaration sourceVariable = ((VariableExp)expression).getReferredVariable();
assert sourceVariable != null;
// assert guardVariables.contains(targetVariable);
// assert guardVariables.contains(sourceVariable);
Node sourceNode = getReferenceNode(sourceVariable);
Node targetNode = boundVariable != null ? getReferenceNode(boundVariable) : Nodes.createNullNode(this, null);
// assert sourceNode.isGuard();
// assert (boundVariable == null) || targetNode.isGuard();
assert sourceNode.isClass();
if (!referredProperty.isIsMany()) {
Edge predicateEdge = sourceNode.getPredicateEdge(referredProperty);
if (predicateEdge == null) {
Edges.createNavigationEdge(sourceNode, referredProperty, targetNode);
}
else {
assert predicateEdge.getTarget() == targetNode;
}
}
}
}
}
private @NonNull Node analyzeVariable(@NonNull Variable variable, @NonNull OCLExpression ownedInit) {
Node initNode = ownedInit.accept(expressionAnalyzer);
assert initNode != null;
if ((ownedInit instanceof OperationCallExp) && initNode.isOperation()) {
if (QVTbaseUtil.isIdentification(((OperationCallExp)ownedInit).getReferredOperation())) {
Node stepNode = Nodes.createRealizedStepNode(this, variable);
Edges.createExpressionEdge(initNode, null, stepNode);
initNode = stepNode;
}
// else if (variable.getType() instanceof CollectionType) {
// Node stepNode = Nodes.ATTRIBUTE.createNode(this, variable, (OperationCallExp)ownedInit);
// Edges.RESULT.createEdge(this, initNode, null, stepNode);
// initNode = stepNode;
// }
else {
// Node stepNode = Nodes.STEP.createNode(this, variable.getName(), (OperationCallExp)ownedInit, initNode);
Node stepNode = Nodes.createLoadedStepNode(this, variable);
Edges.createExpressionEdge(initNode, null, stepNode);
initNode = stepNode;
}
}
initNode.addTypedElement(variable);
addVariableNode(variable, initNode);
return initNode;
}
public @NonNull Node createExtraGuard(@NonNull ClassDatumAnalysis classDatumAnalysis) {
if (extraNodes == null) {
extraNodes = new ArrayList<>();
}
Node extraGuardNode = Nodes.createExtraGuardNode(this, "«extra-" + (extraNodes.size()+1) + "»", classDatumAnalysis);
extraNodes.add(extraGuardNode);
addHeadNode(extraGuardNode);
return extraGuardNode;
}
@Override
public @NonNull VariableNode createVariableNode(@NonNull NodeRole nodeRole, @NonNull VariableDeclaration variable) {
VariableNode variableNode = super.createVariableNode(nodeRole, variable);
addVariableNode(variable, variableNode);
return variableNode;
}
public @Nullable Node getExtraGuard(@NonNull ClassDatumAnalysis classDatumAnalysis) {
if (extraNodes != null) {
for (@NonNull Node extraNode : extraNodes) {
if (extraNode.getClassDatumAnalysis() == classDatumAnalysis) {
return extraNode;
}
}
}
return null;
}
public @NonNull AbstractMapping getMapping() {
return ClassUtil.nonNullState(mappingAction.getMapping());
}
@Override
public @NonNull Iterable<@NonNull MappingAction> getMappingActions() {
return Collections.singletonList(mappingAction);
}
@Override
public @NonNull String getName() {
return String.valueOf(getMapping().getName());
}
public @Nullable Node getNode(@NonNull TypedElement typedElement) {
return variable2node.get(typedElement);
}
/**
* Return the boundExpression if conditionExpression is of the form
* <br>"boundVariable = referenceExpression"
* <br>"referenceExpression = boundVariable".
* <br>"null = referenceExpression"
* <br>"referenceExpression = null".
* <br>"constant-expression = referenceExpression"
* <br>"referenceExpression = constant-expression".
*
* Returns null otherwise.
*/
private @Nullable OCLExpression getPredicateComparisonBoundExpression(@NonNull OCLExpression conditionExpression) {
if (conditionExpression instanceof OperationCallExp) {
OperationCallExp callExp = (OperationCallExp)conditionExpression;
OperationId operationId = callExp.getReferredOperation().getOperationId();
if (PivotUtil.isSameOperation(operationId, getSchedulerConstants().getOclAnyEqualsId())) {
OCLExpression leftExp = callExp.getOwnedSource();
if (leftExp instanceof VariableExp) {
return leftExp;
}
OCLExpression rightExp = callExp.getOwnedArguments().get(0);
if (rightExp instanceof VariableExp) {
return rightExp;
}
IsConstantExpressionVisitor isConstantExpressionVisitor = new IsConstantExpressionVisitor(null);
if (isConstantExpressionVisitor.isConstant(leftExp)) {
return leftExp;
}
if (isConstantExpressionVisitor.isConstant(rightExp)) {
return rightExp;
}
}
}
return null;
}
/**
* Return the referenceExpression if conditionExpression is of the form "boundVariable = referenceExpression" or
* "referenceExpression = boundVariable". Returns null otherwise.
*/
private @Nullable OCLExpression getPredicateComparisonReferenceExpression(@NonNull OCLExpression conditionExpression) {
if (conditionExpression instanceof OperationCallExp) {
OperationCallExp callExp = (OperationCallExp)conditionExpression;
OperationId operationId = callExp.getReferredOperation().getOperationId();
if (PivotUtil.isSameOperation(operationId, getSchedulerConstants().getOclAnyEqualsId())) {
OCLExpression leftExp = callExp.getOwnedSource();
OCLExpression rightExp = callExp.getOwnedArguments().get(0);
if (leftExp instanceof VariableExp) {
return rightExp;
}
else if (rightExp instanceof VariableExp) {
return leftExp;
}
IsConstantExpressionVisitor isConstantExpressionVisitor = new IsConstantExpressionVisitor(null);
if (isConstantExpressionVisitor.isConstant(leftExp)) {
return rightExp;
}
if (isConstantExpressionVisitor.isConstant(rightExp)) {
return leftExp;
}
}
}
return null;
}
public @NonNull Node getReferenceNode(@NonNull VariableDeclaration variable) {
Node node = variable2node.get(variable);
if (node == null) {
if (variable instanceof Variable) {
OCLExpression ownedInit = ((Variable)variable).getOwnedInit();
if (ownedInit != null) {
node = analyzeVariable((Variable) variable, ownedInit);
}
else if (variable.eContainer() instanceof BottomPattern) {
node = Nodes.createLoadedStepNode(this, variable); // FIXME Predicated ??
}
}
}
assert node != null : "No variable2simpleNode entry for " + variable;
return node;
/* if (variable instanceof RealizedVariable) {
return Nodes.REALIZED_VARIABLE.createNode(this, (RealizedVariable)variable);
}
else if (variable.eContainer() instanceof BottomPattern) {
return Nodes.UNREALIZED_VARIABLE.createNode(this, variable);
}
else {
return new GuardVariableNode(this, variable);
} */
}
public @NonNull Node getUnknownNode(@NonNull TypedElement typedElement) {
assert !(typedElement instanceof Property); // Property entries should be AttributeNodes
Node node = getNode(typedElement);
if (node == null) {
node = Nodes.createUnknownNode(this, ClassUtil.nonNullState(typedElement.getType().toString()), typedElement);
// node2node.put(typedElement, node);
}
return node;
}
public void initialize() {
//
// Create the BLUE/CYAN guard nodes.
//
analyzeGuardVariables();
//
// Create the GREEN realized nodes.
//
analyzeRealizedVariables();
//
// Create the initialization/predicate/computation nodes and edges
//
analyzeInitializers(guardPatterns);
analyzeInitializers(bottomPatterns);
analyzePredicates(guardPatterns);
analyzePredicates(bottomPatterns);
analyzeAssignmentValues();
analyzeComplexPredicates();
//
getHeadNodes();
}
/**
* Return true if the navigation from sourceNode using source2targetProperty corresponds to a PropertyAssigmment,
*/
public boolean isPropertyAssignment(@NonNull Node sourceNode, @NonNull Property source2targetProperty) {
if (sourceNode.isRealized()) {
for (@NonNull NavigationAssignment navigationAssignment : navigationAssignments) {
Property navigationProperty = QVTcoreBaseUtil.getTargetProperty(navigationAssignment);
if (source2targetProperty == navigationProperty) { // ??? opposites ??? do they even exist ???
Node slotNode = expressionAnalyzer.analyze(navigationAssignment.getSlotExpression());
if (slotNode == sourceNode) {
return true;
}
}
}
}
return false;
}
public void registerConsumptionsAndProductions() {
for (@NonNull Node newNode : getNewNodes()) {
ClassDatumAnalysis classDatumAnalysis = newNode.getClassDatumAnalysis();
classDatumAnalysis.addProduction(this, newNode);
ClassDatum classDatum = classDatumAnalysis.getClassDatum();
for (@SuppressWarnings("null")@NonNull AbstractAction consumingAction : classDatum.getRequiredBy()) {
MappingRegion consumingRegion = multiRegion.getMappingRegion(consumingAction);
assert consumingRegion != null;
for (@NonNull Node consumingNode : consumingRegion.getOldNodes()) {
if (consumingNode.getCompleteClass() == classDatumAnalysis.getCompleteClass()) { // FIXME inheritance
classDatumAnalysis.addConsumption(consumingRegion, consumingNode);
}
}
}
}
for (@NonNull Node predicatedNode : getOldNodes()) {
ClassDatumAnalysis classDatumAnalysis = predicatedNode.getClassDatumAnalysis();
classDatumAnalysis.addConsumption(this, predicatedNode);
ClassDatum classDatum = classDatumAnalysis.getClassDatum();
for (@SuppressWarnings("null")@NonNull AbstractAction introducingAction : classDatum.getProducedBy()) {
MappingRegion producingRegion = multiRegion.getMappingRegion(introducingAction);
assert producingRegion != null;
for (@NonNull Node newNode : producingRegion.getNewNodes()) {
if (newNode.getCompleteClass() == classDatumAnalysis.getCompleteClass()) { // FIXME inheritance
classDatumAnalysis.addProduction(producingRegion, newNode);
}
}
}
}
}
}