blob: ff4eeae3d335528c6a24343223ff07f86e624a7f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Willink Transformations 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:
* E.D.Willink - initial API and implementation based on MtcBroker
******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvti.analysis;
import java.io.IOException;
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.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteClass;
import org.eclipse.ocl.pivot.CompleteModel;
import org.eclipse.ocl.pivot.DataType;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.NamedElement;
import org.eclipse.ocl.pivot.NavigationCallExp;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.OppositePropertyCallExp;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.TypedElement;
import org.eclipse.ocl.pivot.internal.prettyprint.PrettyPrinter;
import org.eclipse.ocl.pivot.util.Visitable;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.ocl.pivot.utilities.Nameable;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.ocl.pivot.utilities.TracingOption;
import org.eclipse.ocl.pivot.utilities.TreeIterable;
import org.eclipse.qvtd.compiler.CompilerConstants;
import org.eclipse.qvtd.compiler.CompilerProblem;
import org.eclipse.qvtd.compiler.CompilerStep;
import org.eclipse.qvtd.compiler.internal.qvtb2qvts.MappingProblem;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphEdge;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphElement;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder.GraphNode;
import org.eclipse.qvtd.pivot.qvtbase.graphs.ToGraphHelper;
import org.eclipse.qvtd.pivot.qvtbase.utilities.StandardLibraryHelper;
import org.eclipse.qvtd.pivot.qvtimperative.DeclareStatement;
import org.eclipse.qvtd.pivot.qvtimperative.EntryPoint;
import org.eclipse.qvtd.pivot.qvtimperative.GuardParameter;
import org.eclipse.qvtd.pivot.qvtimperative.Mapping;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatement;
import org.eclipse.qvtd.pivot.qvtimperative.NewStatementPart;
import org.eclipse.qvtd.pivot.qvtimperative.ObservableStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SetStatement;
import org.eclipse.qvtd.pivot.qvtimperative.SpeculateStatement;
import org.eclipse.qvtd.pivot.qvtimperative.Statement;
import org.eclipse.qvtd.pivot.qvtimperative.util.AbstractExtendingQVTimperativeVisitor;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.DomainUsage;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.Graphable;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleUtil;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.ToGraphVisitor2;
/**
* QVTiProductionConsumption supports a design rule check on the QVTi model to verify that consumed edges are produced and that notify/observe annotations ensure
* that not-necessarily-ready consumptions wait for their corresponding productions.
*/
public class QVTiProductionConsumption extends AbstractExtendingQVTimperativeVisitor<@Nullable Object, @NonNull Resource> implements Graphable
{
public static final @NonNull TracingOption SUMMARY = new TracingOption(CompilerConstants.PLUGIN_ID, "qvti/check/summary");
private abstract static class AbstractGraphEdge implements GraphEdge
{
@Override
public @NonNull String getColor() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull GraphNode getEdgeSource() {
throw new UnsupportedOperationException();
}
@Override
public @NonNull GraphNode getEdgeTarget() {
throw new UnsupportedOperationException();
}
}
private abstract static class AbstractGraphNode implements GraphNode
{
@Override
public @NonNull String getColor() {
throw new UnsupportedOperationException();
}
}
protected static class ConsumptionEdge extends AbstractGraphEdge
{
private final boolean isObserve;
protected ConsumptionEdge(boolean isObserve) {
this.isObserve = isObserve;
}
@Override
public void appendEdgeAttributes(@NonNull ToGraphHelper toGraphHelper, @NonNull String sourceName, @NonNull String targetName) {
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setColor(isObserve ? "brown" : "orange");
s.setPenwidth(isObserve ? 2 : 1);
s.appendAttributedEdge(sourceName, this, targetName);
}
}
protected static class ProductionEdge extends AbstractGraphEdge
{
private final boolean isNotify;
protected ProductionEdge(boolean isNotify) {
this.isNotify = isNotify;
}
@Override
public void appendEdgeAttributes(@NonNull ToGraphHelper toGraphHelper, @NonNull String sourceName, @NonNull String targetName) {
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setColor(isNotify ? "brown" : "orange");
s.setPenwidth(isNotify ? 2 : 1);
s.appendAttributedEdge(sourceName, this, targetName);
}
}
protected static class ConnectionEdge extends AbstractGraphEdge
{
protected ConnectionEdge() {}
@Override
public void appendEdgeAttributes(@NonNull ToGraphHelper toGraphHelper, @NonNull String sourceName, @NonNull String targetName) {
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setColor("cyan");
s.setPenwidth(1);
s.appendAttributedEdge(sourceName, this, targetName);
}
}
/**
* PassRange captures a first..last range of passes.
*/
private static class PassRange
{
public static @NonNull PassRange create(@NonNull Element element) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(element);
int firstPass = mapping.getFirstPass();
Integer lastPass = mapping.getLastPass();
return new PassRange(firstPass, lastPass == null ? firstPass : lastPass);
}
public static @NonNull PassRange create(@NonNull List<? extends @NonNull Element> elements) {
PassRange passRange = new PassRange();
for (@NonNull Element element : elements) {
passRange = create(element).max(passRange);
}
return passRange;
}
private final int first;
private final int last;
public PassRange() {
this.first = Integer.MAX_VALUE;
this.last = Integer.MIN_VALUE;
}
public PassRange(int first, int last) {
this.first = first;
this.last = last;
assert first <= last;
}
@Override
public boolean equals(Object obj) {
PassRange that = (PassRange) obj;
return (this.first == that.first) && (this.last == that.last);
}
@Override
public int hashCode() {
return this.first * 7 + this.last * 97;
}
// public boolean isDefined() {
// return this.first <= this.last;
// }
public @NonNull PassRange max(@NonNull PassRange passRange) {
if (this.equals(passRange)) {
return this;
}
return new PassRange(Math.min(this.first, passRange.first), Math.max(this.last, passRange.last));
}
public boolean precedes(@NonNull PassRange passRange) {
return this.last < passRange.first;
}
@Override
public @NonNull String toString() {
if (first < last) {
return "[" + first + ".." + last + "]" ;
}
else if (first == last) {
return "[" + first + "]" ;
}
else {
return "[]";
}
}
}
/**
* BasePropertyAnalysis aggregates the AccessAnalysis for each access using a Property or its opposite.
*/
private class BasePropertyAnalysis implements Nameable
{
/**
* The base/normalized/primary property for the edge production/consumption.
*/
protected final @NonNull Property baseProperty;
/**
* Name of the base/normalized/primary property and its opposite.
*/
protected final @NonNull String name;
/**
* The various accesses that conform to the baseProperty.
*/
protected final @NonNull Map<@NonNull CompleteClass, @NonNull Map<@NonNull CompleteClass, @NonNull AccessAnalysis>> sourceClass2targetClass2accessAnalysis = new HashMap<>();
/**
* The 'connection' that produces each consumed access.
* A BasePropertyAnalysis 'references' a ConnectionAnalysis.
*/
protected final @NonNull Map<@NonNull AccessAnalysis, @Nullable ConnectionAnalysis> consumingAnalysis2connectionAnalysis = new HashMap<>();
/**
* The 'connection's that consume each produced access.
* A BasePropertyAnalysis 'references' many ConnectionAnalysis.
*/
protected final @NonNull Map<@NonNull AccessAnalysis, @Nullable List<@NonNull ConnectionAnalysis>> producingAnalysis2connectionAnalyses = new HashMap<>();
/**
* The 'connection' that aggregates a set of productions that conform to a consumption.
* A BasePropertyAnalysis 'composes' a ConnectionAnalysis.
*/
protected final @NonNull Map<@NonNull Set<@NonNull AccessAnalysis>, @NonNull ConnectionAnalysis> producingAnalyses2connectionAnalysis = new HashMap<>();
public BasePropertyAnalysis(@NonNull Property baseProperty) {
this.baseProperty = baseProperty;
StringBuilder s = new StringBuilder();
s.append(baseProperty.getOwningClass().getName());
s.append("::");
s.append(baseProperty.getName());
if (baseProperty.isIsMany()) {
s.append(/*baseProperty.isIsRequired() ? "[+]" :*/ "[*]");
}
else {
s.append(baseProperty.isIsRequired() ? "[1]" : "[?]");
}
Property oppositeProperty = baseProperty.getOpposite();
if (oppositeProperty != null) {
s.append(" <=> ");
s.append(oppositeProperty.getOwningClass().getName());
s.append("::");
s.append(oppositeProperty.getName());
if (baseProperty.isIsMany()) {
s.append(/*oppositeProperty.isIsRequired() ? "[+]" :*/ "[*]");
}
else {
s.append(oppositeProperty.isIsRequired() ? "[1]" : "[?]");
}
}
this.name = s.toString();
assert baseProperty == QVTscheduleUtil.getPrimaryProperty(baseProperty);
}
/**
* Add all the accesses of basePropertyAnalysis to this BasePropertyAnalysis. This is used to create the
* pseudo-Property oclContainer from the actual containment properties.
*/
public void accumulate(@NonNull BasePropertyAnalysis basePropertyAnalysis) {
for (@NonNull AccessAnalysis producingAnalysis : basePropertyAnalysis.producingAnalysis2connectionAnalyses.keySet()) {
for (@NonNull NamedElement producer : producingAnalysis.producers) {
addProducer(producer, producingAnalysis.sourceClass, producingAnalysis.property, producingAnalysis.targetClass);
}
}
for (@NonNull AccessAnalysis consumingAnalysis : basePropertyAnalysis.consumingAnalysis2connectionAnalysis.keySet()) {
for (@NonNull NavigationCallExp consumer : consumingAnalysis.consumers) {
addConsumer(consumer, consumingAnalysis.sourceClass, consumingAnalysis.property, consumingAnalysis.targetClass);
}
}
}
public void addConsumer(@NonNull NavigationCallExp navigationCallExp, @NonNull CompleteClass sourceClass, @NonNull Property property, @NonNull CompleteClass targetClass) {
AccessAnalysis accessAnalysis = getAccessAnalysis(sourceClass, property, targetClass);
accessAnalysis.addConsumer(navigationCallExp);
if (!consumingAnalysis2connectionAnalysis.containsKey(accessAnalysis)) {
consumingAnalysis2connectionAnalysis.put(accessAnalysis, null);
}
}
public void addProducer(@NonNull NamedElement producer, @NonNull CompleteClass sourceClass, @NonNull Property property, @NonNull CompleteClass targetClass) {
AccessAnalysis accessAnalysis = getAccessAnalysis(sourceClass, property, targetClass);
accessAnalysis.addProducer(producer);
if (!producingAnalysis2connectionAnalyses.containsKey(accessAnalysis)) {
producingAnalysis2connectionAnalyses.put(accessAnalysis, null);
}
}
public void analyze() {
for (@NonNull AccessAnalysis consumingAnalysis : consumingAnalysis2connectionAnalysis.keySet()) {
Set<@NonNull AccessAnalysis> conformingProducingAnalyses = null;
for (@NonNull AccessAnalysis producingAnalysis : producingAnalysis2connectionAnalyses.keySet()) {
if (isConforming(producingAnalysis, consumingAnalysis)) {
if (conformingProducingAnalyses == null) {
conformingProducingAnalyses = new HashSet<>();
}
conformingProducingAnalyses.add(producingAnalysis);
}
}
if (conformingProducingAnalyses != null) {
ConnectionAnalysis connectionAnalysis = getConnectionAnalysis(conformingProducingAnalyses);
connectionAnalysis.addConsumingAnalysis(consumingAnalysis);
consumingAnalysis2connectionAnalysis.put(consumingAnalysis, connectionAnalysis);
}
}
}
private void appendComplexPropertyAnalysis(@NonNull ToGraph toGraph, @NonNull Map<@NonNull Mapping, @NonNull MappingNode> mapping2mappingNode) {
Map<@NonNull ConnectionAnalysis, @NonNull ConnectionAnalysisNode> connectionAnalysis2connectionAnalysisNode = new HashMap<>();
Map<@NonNull AccessAnalysis, @NonNull AccessAnalysisNode> consumingAnalysis2accessAnalysisNode = new HashMap<>();
Map<@NonNull AccessAnalysis, @NonNull AccessAnalysisNode> producingAnalysis2accessAnalysisNode = new HashMap<>();
StringBuilder sLabel = new StringBuilder();
sLabel.append(baseProperty.getOwningClass().getName());
sLabel.append("::");
sLabel.append(baseProperty.getName());
String label = sLabel.toString();
GraphStringBuilder context = toGraph.context;
context.setLabel(label);
context.setColor("blue");
context.setStyle(baseProperty.getType() instanceof DataType ? "rounded" : "solid");
context.setPenwidth(2);
context.pushCluster();
for (@NonNull ConnectionAnalysis connectionAnalysis : producingAnalyses2connectionAnalysis.values()) {
getConnectionAnalysisNode(toGraph, connectionAnalysis, connectionAnalysis2connectionAnalysisNode);
}
for (@NonNull AccessAnalysis producingAnalysis : producingAnalysis2connectionAnalyses.keySet()) {
getAccessAnalysisNode(toGraph, producingAnalysis, producingAnalysis2accessAnalysisNode, false);
}
for (@NonNull AccessAnalysis consumingAnalysis : consumingAnalysis2connectionAnalysis.keySet()) {
getAccessAnalysisNode(toGraph, consumingAnalysis, consumingAnalysis2accessAnalysisNode, true);
}
context.popCluster();
for (@NonNull ConnectionAnalysis connectionAnalysis : connectionAnalysis2connectionAnalysisNode.keySet()) {
ConnectionAnalysisNode connectionAnalysisNode = connectionAnalysis2connectionAnalysisNode.get(connectionAnalysis);
assert connectionAnalysisNode != null;
for (@NonNull AccessAnalysis consumingAnalysis : connectionAnalysis.consumingAnalyses) {
AccessAnalysisNode consumingAnalysisNode = consumingAnalysis2accessAnalysisNode.get(consumingAnalysis);
assert consumingAnalysisNode != null;
context.appendEdge(toGraph, connectionAnalysisNode, new ConnectionEdge(), consumingAnalysisNode);
// for (@NonNull NavigationCallExp consumer : consumingAnalysis.consumers) {
// MappingNode mappingNode = getMappingNode(mapping2mappingNode, consumer);
// context.appendEdge(toGraph, consumingAnalysisNode, new ConsumptionEdge(isObserve(consumer)), mappingNode);
// }
}
for (@NonNull AccessAnalysis producingAnalysis : connectionAnalysis.producingAnalyses) {
AccessAnalysisNode producingAnalysisNode = producingAnalysis2accessAnalysisNode.get(producingAnalysis);
assert producingAnalysisNode != null;
context.appendEdge(toGraph, producingAnalysisNode, new ConnectionEdge(), connectionAnalysisNode);
// for (@NonNull NamedElement producer : producingAnalysis.producers) {
// MappingNode mappingNode = getMappingNode(mapping2mappingNode, producer);
// context.appendEdge(toGraph, mappingNode, new ProductionEdge(isNotify(producer)), producingAnalysisNode);
// }
}
}
for (@NonNull AccessAnalysis producingAnalysis : producingAnalysis2connectionAnalyses.keySet()) {
AccessAnalysisNode producingAnalysisNode = producingAnalysis2accessAnalysisNode.get(producingAnalysis);
assert producingAnalysisNode != null;
for (@NonNull NamedElement producer : producingAnalysis.producers) {
MappingNode mappingNode = getMappingNode(mapping2mappingNode, producer);
context.appendEdge(toGraph, mappingNode, new ProductionEdge(isNotify(producer)), producingAnalysisNode);
}
}
for (@NonNull AccessAnalysis consumingAnalysis : consumingAnalysis2connectionAnalysis.keySet()) {
AccessAnalysisNode consumingAnalysisNode = consumingAnalysis2accessAnalysisNode.get(consumingAnalysis);
assert consumingAnalysisNode != null;
for (@NonNull NavigationCallExp consumer : consumingAnalysis.consumers) {
MappingNode mappingNode = getMappingNode(mapping2mappingNode, consumer);
context.appendEdge(toGraph, consumingAnalysisNode, new ConsumptionEdge(isObserve(consumer)), mappingNode);
}
}
}
public void appendNode(@NonNull ToGraph toGraph, @NonNull Map<@NonNull Mapping, @NonNull MappingNode> mapping2mappingNode) {
Set<@NonNull AccessAnalysis> allAccessAnalyses = new HashSet<>(consumingAnalysis2connectionAnalysis.keySet());
allAccessAnalyses.addAll(producingAnalysis2connectionAnalyses.keySet());
if (allAccessAnalyses.size() <= 1) {
appendSimplePropertyAnalysis(toGraph, mapping2mappingNode, allAccessAnalyses.iterator().next());
}
else {
appendComplexPropertyAnalysis(toGraph, mapping2mappingNode);
}
}
private void appendSimplePropertyAnalysis(@NonNull ToGraph toGraph, @NonNull Map<@NonNull Mapping, @NonNull MappingNode> mapping2mappingNode, @NonNull AccessAnalysis accessAnalysis) {
GraphStringBuilder context = toGraph.context;
AccessAnalysisNode accessAnalysisNode = new AccessAnalysisNode(accessAnalysis, null);
context.appendNode(toGraph, accessAnalysisNode);
for (@NonNull NavigationCallExp consumer : accessAnalysis.consumers) {
MappingNode mappingNode = getMappingNode(mapping2mappingNode, consumer);
ConsumptionEdge consumptionEdge = new ConsumptionEdge(isObserve(consumer));
context.appendEdge(toGraph, accessAnalysisNode, consumptionEdge, mappingNode);
}
for (@NonNull NamedElement producer : accessAnalysis.producers) {
MappingNode mappingNode = getMappingNode(mapping2mappingNode, producer);
ProductionEdge productionEdge = new ProductionEdge(isNotify(producer));
context.appendEdge(toGraph, mappingNode, productionEdge, accessAnalysisNode);
}
}
public @NonNull AccessAnalysis getAccessAnalysis(@NonNull CompleteClass sourceClass, @NonNull Property property, @NonNull CompleteClass targetClass) {
assert !PivotUtil.isDataType(sourceClass);
Map<@NonNull CompleteClass, @NonNull AccessAnalysis> targetClass2accessAnalysis = sourceClass2targetClass2accessAnalysis.get(sourceClass);
if (targetClass2accessAnalysis == null) {
targetClass2accessAnalysis = new HashMap<>();
sourceClass2targetClass2accessAnalysis.put(sourceClass, targetClass2accessAnalysis);
}
AccessAnalysis accessAnalysis = targetClass2accessAnalysis.get(targetClass);
if (accessAnalysis == null) {
accessAnalysis = new AccessAnalysis(this, sourceClass, property, targetClass);
targetClass2accessAnalysis.put(targetClass, accessAnalysis);
}
return accessAnalysis;
}
protected @NonNull AccessAnalysisNode getAccessAnalysisNode(@NonNull ToGraph toGraph, @NonNull AccessAnalysis accessAnalysis,
@NonNull Map<@NonNull AccessAnalysis, @NonNull AccessAnalysisNode> accessAnalysis2accessAnalysisNode, boolean isConsumption) {
AccessAnalysisNode accessAnalysisNode = accessAnalysis2accessAnalysisNode.get(accessAnalysis);
if (accessAnalysisNode == null) {
accessAnalysisNode = new AccessAnalysisNode(accessAnalysis, isConsumption);
accessAnalysis2accessAnalysisNode.put(accessAnalysis, accessAnalysisNode);
toGraph.context.appendNode(toGraph, accessAnalysisNode);
}
return accessAnalysisNode;
}
protected @NonNull ConnectionAnalysis getConnectionAnalysis(@NonNull Set<@NonNull AccessAnalysis> producingAnalyses) {
ConnectionAnalysis connectionAnalysis = producingAnalyses2connectionAnalysis.get(producingAnalyses);
if (connectionAnalysis == null) {
connectionAnalysis = new ConnectionAnalysis(this, producingAnalyses);
producingAnalyses2connectionAnalysis.put(producingAnalyses, connectionAnalysis);
for (@NonNull AccessAnalysis producingAnalysis : producingAnalyses) {
List<@NonNull ConnectionAnalysis> connectionAnalyses = producingAnalysis2connectionAnalyses.get(producingAnalysis);
if (connectionAnalyses == null) {
connectionAnalyses = new ArrayList<>();
producingAnalysis2connectionAnalyses.put(producingAnalysis, connectionAnalyses);
}
connectionAnalyses.add(connectionAnalysis);
}
}
return connectionAnalysis;
}
protected @NonNull ConnectionAnalysisNode getConnectionAnalysisNode(@NonNull ToGraph toGraph, @NonNull ConnectionAnalysis connectionAnalysis,
Map<@NonNull ConnectionAnalysis, @NonNull ConnectionAnalysisNode> connectionAnalysis2connectionAnalysisNode) {
ConnectionAnalysisNode connectionAnalysisNode = connectionAnalysis2connectionAnalysisNode.get(connectionAnalysis);
if (connectionAnalysisNode == null) {
connectionAnalysisNode = new ConnectionAnalysisNode(connectionAnalysis);
connectionAnalysis2connectionAnalysisNode.put(connectionAnalysis, connectionAnalysisNode);
toGraph.context.appendNode(toGraph, connectionAnalysisNode);
}
return connectionAnalysisNode;
}
protected @NonNull MappingNode getMappingNode(@NonNull Map<@NonNull Mapping, @NonNull MappingNode> mapping2mappingNode, @NonNull NamedElement element) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(element);
assert mapping != null;
MappingNode mappingNode = mapping2mappingNode.get(mapping);
if (mappingNode == null) {
mappingNode = new MappingNode(mapping);
mapping2mappingNode.put(mapping, mappingNode);
}
return mappingNode;
}
@Override
public @NonNull String getName() {
return name;
}
private boolean isConforming(@NonNull CompleteClass producingSourceClass, @NonNull Property producingProperty, @NonNull CompleteClass producingTargetClass,
@NonNull CompleteClass consumingSourceClass, @NonNull Property consumingProperty, @NonNull CompleteClass consumingTargetClass) {
boolean conformingSource = QVTscheduleUtil.conformsToClassOrBehavioralClass(producingSourceClass, consumingSourceClass)
|| QVTscheduleUtil.conformsToClassOrBehavioralClass(consumingSourceClass, producingSourceClass);
boolean conformingTarget = QVTscheduleUtil.conformsToClassOrBehavioralClass(producingTargetClass, consumingTargetClass)
|| QVTscheduleUtil.conformsToClassOrBehavioralClass(consumingTargetClass, producingTargetClass);
return conformingSource && conformingTarget;
}
protected boolean isConforming(@NonNull AccessAnalysis producingAnalysis, @NonNull AccessAnalysis consumingAnalysis) {
Property consumingProperty = consumingAnalysis.property;
CompleteClass consumingSourceClass;
CompleteClass consumingTargetClass;
if (consumingProperty == baseProperty) {
consumingSourceClass = consumingAnalysis.sourceClass;
consumingTargetClass = consumingAnalysis.targetClass;
}
else {
consumingSourceClass = consumingAnalysis.targetClass;
consumingTargetClass = consumingAnalysis.sourceClass;
}
Property producingProperty = producingAnalysis.property;
CompleteClass producingSourceClass;
CompleteClass producingTargetClass;
if (producingProperty == baseProperty) {
producingSourceClass = producingAnalysis.sourceClass;
producingTargetClass = producingAnalysis.targetClass;
}
else {
producingSourceClass = producingAnalysis.targetClass;
producingTargetClass = producingAnalysis.sourceClass;
}
return isConforming(producingSourceClass, producingProperty, producingTargetClass, consumingSourceClass, consumingProperty, consumingTargetClass);
}
private boolean isNotify(@NonNull NamedElement producer) {
if (producer instanceof SetStatement) {
return ((SetStatement)producer).isIsNotify();
}
else {
return false;
}
}
private boolean isObserve(@NonNull NavigationCallExp callExp) {
Statement statement = QVTimperativeUtil.getContainingStatement(callExp);
if (statement instanceof ObservableStatement) {
List<Property> observedProperties = ((ObservableStatement)statement).basicGetObservedProperties();
if (observedProperties != null) {
boolean contains1 = observedProperties.contains(baseProperty);
boolean contains2 = observedProperties.contains(baseProperty.getOpposite());
if (contains1) {
return true;
}
else if (contains2) {
return true;
}
else {
return false;
}
}
}
return false;
}
@Override
public @NonNull String toString() {
return name;
}
public void validate(@Nullable StringBuilder s) {
if (s != null) {
s.append("\n " + name);
for (@NonNull AccessAnalysis producingAnalysis : producingAnalysis2connectionAnalyses.keySet()) {
List<@NonNull ConnectionAnalysis> connectionAnalyses = producingAnalysis2connectionAnalyses.get(producingAnalysis);
if (connectionAnalyses == null) {
for (@NonNull NamedElement producer : producingAnalysis.producers) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(producer);
s.append("\n unconnected " + mapping.getName() + " " + PassRange.create(mapping) + " " + producer);
}
}
}
for (@NonNull ConnectionAnalysis connectionAnalysis : producingAnalyses2connectionAnalysis.values()) {
boolean needsNotify = connectionAnalysis.needsNotify();
s.append("\n connect produce " + connectionAnalysis.productionPassRange + " consume " + connectionAnalysis.consumptionPassRange + (needsNotify ? " needsNotify" : " not-needsNotify"));
for (@NonNull AccessAnalysis producingAnalysis : connectionAnalysis.producingAnalyses) {
boolean needsNotify2 = false;
List<@NonNull ConnectionAnalysis> connectionAnalyses2 = producingAnalysis2connectionAnalyses.get(producingAnalysis);
assert connectionAnalyses2 != null;
for (@NonNull ConnectionAnalysis connectionAnalysis2 : connectionAnalyses2) {
if (connectionAnalysis2.needsNotify()) {
needsNotify2 = true;
}
}
s.append("\n produce " + producingAnalysis.name + " " + producingAnalysis.getProductionPassRange() + (needsNotify2 ? " needsNotify" : " not-needsNotify"));
for (@NonNull NamedElement producer : producingAnalysis.producers) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(producer);
s.append("\n in " + mapping.getName() + " " + PassRange.create(mapping) + " " + producer);
}
}
for (@NonNull AccessAnalysis consumingAnalysis : connectionAnalysis.consumingAnalyses) {
boolean needsObserve = !connectionAnalysis.getProductionPassRange().precedes(consumingAnalysis.getConsumptionPassRange());
s.append("\n consume " + consumingAnalysis.name + " " + consumingAnalysis.getConsumptionPassRange() + (needsObserve ? " needsObserve" : " not-needsObserve"));
for (@NonNull NavigationCallExp consumer : consumingAnalysis.consumers) {
Mapping mapping = QVTimperativeUtil.getContainingMapping(consumer);
s.append("\n in " + mapping.getName() + " " + PassRange.create(mapping) + " " + consumer);
}
}
}
}
List<@NonNull AccessAnalysis> producingAnalyses = new ArrayList<>(producingAnalysis2connectionAnalyses.keySet());
Collections.sort(producingAnalyses, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull AccessAnalysis producingAnalysis : producingAnalyses) {
validateNotifies(s, producingAnalysis);
}
List<@NonNull AccessAnalysis> consumingAnalyses = new ArrayList<>(consumingAnalysis2connectionAnalysis.keySet());
Collections.sort(producingAnalyses, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull AccessAnalysis consumingAnalysis : consumingAnalyses) {
validateObserves(s, consumingAnalysis);
}
}
private void validateNotifies(@Nullable StringBuilder s, @NonNull AccessAnalysis producingAnalysis) {
List<@NonNull ConnectionAnalysis> connectionAnalyses = producingAnalysis2connectionAnalyses.get(producingAnalysis);
if (connectionAnalyses != null) { // consumption is optional (may be produced only for output)
PassRange overallProductionPassRange = new PassRange();
PassRange overallConsumptionPassRange = new PassRange();
boolean needsNotify = false;
for (@NonNull ConnectionAnalysis connectionAnalysis : connectionAnalyses) {
PassRange productionPassRange = connectionAnalysis.getProductionPassRange();
PassRange consumptionPassRange = connectionAnalysis.getConsumptionPassRange();
if (!productionPassRange.precedes(consumptionPassRange)) {
needsNotify = true;
}
overallProductionPassRange = overallProductionPassRange.max(productionPassRange);
overallConsumptionPassRange = overallConsumptionPassRange.max(consumptionPassRange);
}
//
// NB. If any consumption observes, all productions must notify.
//
// boolean needsNotify = !productionPassRange.precedes(consumptionPassRange);
for (@NonNull NamedElement producer : producingAnalysis.producers) {
if (!(producer instanceof GuardParameter)) { // GuardParameter are implicitly notified; no need to check
boolean isNotify = isNotify(producer);
if (isNotify != needsNotify) {
Property property = producingAnalysis.property;
StringBuilder sProblem = new StringBuilder();
sProblem.append("The production of: ");
sProblem.append(PrettyPrinter.print(property));
sProblem.append("\n\tat ");
sProblem.append(overallProductionPassRange);
sProblem.append(" should");
if (isNotify) {
sProblem.append(" not");
}
sProblem.append(" be notified for use at ");
sProblem.append(overallConsumptionPassRange);
Mapping mapping = QVTimperativeUtil.getContainingMapping(producer);
compilerStep.addProblem(new MappingProblem(CompilerProblem.Severity.WARNING, mapping, sProblem.toString()));
if (s != null) {
s.append("\n BAD " + sProblem);
}
}
}
}
}
}
private void validateObserves(@Nullable StringBuilder s, @NonNull AccessAnalysis consumingAnalysis) {
ConnectionAnalysis connectionAnalysis = consumingAnalysis2connectionAnalysis.get(consumingAnalysis);
PassRange productionRange = connectionAnalysis != null ? connectionAnalysis.getProductionPassRange() : null;
for (@NonNull NavigationCallExp consumer : consumingAnalysis.consumers) {
if (productionRange == null) {
OCLExpression ownedSource = PivotUtil.getOwnedSource(consumer);
DomainUsage sourceUsage = domainUsageAnalysis.getUsage(ownedSource);
if (!sourceUsage.isThis()) {
Property property = consumingAnalysis.property;
StringBuilder sProblem = new StringBuilder();
sProblem.append("No possible producer of: ");
sProblem.append(PrettyPrinter.print(property));
sProblem.append("\n\tcan satisfy expression: ");
sProblem.append(PrettyPrinter.print(consumer));
sProblem.append("\n\tnavigation source type: ");
sProblem.append(PrettyPrinter.print(consumingAnalysis.sourceClass.getPrimaryClass()));
sProblem.append("\n\tnavigation target type: ");
sProblem.append(PrettyPrinter.print(consumingAnalysis.targetClass.getPrimaryClass()));
Mapping mapping = QVTimperativeUtil.getContainingMapping(consumer);
compilerStep.addProblem(new MappingProblem(CompilerProblem.Severity.WARNING, mapping, sProblem.toString()));
if (s != null) {
s.append("\n BAD " + sProblem);
}
}
}
else if (!(consumer.eContainer() instanceof SpeculateStatement)) {
PassRange consumerPassRange = PassRange.create(consumer);
boolean needsObserve = !productionRange.precedes(consumerPassRange);
boolean isObserve = isObserve(consumer);
if (isObserve != needsObserve) {
Property property = consumingAnalysis.property;
StringBuilder sProblem = new StringBuilder();
sProblem.append("The production of: ");
sProblem.append(PrettyPrinter.print(property));
sProblem.append("\n\tfor expression: ");
sProblem.append(PrettyPrinter.print(consumer));
sProblem.append("\n\tat ");
sProblem.append(productionRange);
sProblem.append(" should");
if (isObserve) {
sProblem.append(" not");
}
sProblem.append(" be observed at ");
sProblem.append(consumerPassRange);
Mapping mapping = QVTimperativeUtil.getContainingMapping(consumer);
compilerStep.addProblem(new MappingProblem(CompilerProblem.Severity.WARNING, mapping, sProblem.toString()));
if (s != null) {
s.append("\n BAD " + sProblem);
}
}
}
}
}
}
private class MappingNode extends AbstractGraphNode
{
protected final @NonNull Mapping mapping;
protected MappingNode(@NonNull Mapping mapping) {
this.mapping = mapping;
}
@Override
public void appendNode(@NonNull ToGraphHelper toGraphHelper, @NonNull String nodeName) {
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setLabel(mapping.getName() + "\n" + PassRange.create(mapping));
s.setShape("rectangle");
s.setStyle("solid");
s.setColor("green");
s.setPenwidth(2);
s.appendAttributedNode(nodeName);
}
}
private class AccessAnalysisNode extends AbstractGraphNode
{
protected final @NonNull AccessAnalysis accessAnalysis;
protected final @Nullable Boolean isConsumption;
protected AccessAnalysisNode(@NonNull AccessAnalysis accessAnalysis, @Nullable Boolean isConsumption) {
this.accessAnalysis = accessAnalysis;
this.isConsumption = isConsumption;
}
@Override
public void appendNode(@NonNull ToGraphHelper toGraphHelper, @NonNull String nodeName) {
Property property = accessAnalysis.property;
Type type = property.getType();
PassRange productionPassRange = accessAnalysis.getProductionPassRange();
PassRange consumptionPassRange = accessAnalysis.getConsumptionPassRange();
StringBuilder sLabel = new StringBuilder();
sLabel.append(accessAnalysis.sourceClass.getName());
sLabel.append("::");
sLabel.append(property.getName());
sLabel.append("\n: ");
sLabel.append(accessAnalysis.targetClass.getName());
if (property.isIsMany()) {
sLabel.append(/*property.isIsRequired() ? "[+]" :*/ "[*]");
}
else {
sLabel.append(property.isIsRequired() ? "[1]" : "[?]");
}
sLabel.append("\n");
if (isConsumption == null) {
sLabel.append(productionPassRange);
sLabel.append(" => ");
sLabel.append(consumptionPassRange);
}
else {
sLabel.append(isConsumption ? consumptionPassRange : productionPassRange);
}
String label = sLabel.toString();
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setLabel(label);
s.setShape("rectangle");
s.setStyle(type instanceof DataType ? "rounded" : "solid");
s.setColor(isConsumption == null ? "blue" : "cyan");
s.setPenwidth(2);
s.appendAttributedNode(nodeName);
}
}
private class ConnectionAnalysisNode extends AbstractGraphNode
{
protected final @NonNull ConnectionAnalysis connectionAnalysis;
protected ConnectionAnalysisNode(@NonNull ConnectionAnalysis connectionAnalysis) {
this.connectionAnalysis = connectionAnalysis;
}
@Override
public void appendNode(@NonNull ToGraphHelper toGraphHelper, @NonNull String nodeName) {
BasePropertyAnalysis basePropertyAnalysis = connectionAnalysis.basePropertyAnalysis;
Property baseProperty = basePropertyAnalysis.baseProperty;
StringBuilder sLabel = new StringBuilder();
sLabel.append(baseProperty.getOwningClass().getName());
sLabel.append("::");
sLabel.append(baseProperty.getName());
String label = sLabel.toString();
GraphStringBuilder s = toGraphHelper.getGraphStringBuilder();
s.setLabel(label);
s.setShape("ellipse");
s.setStyle("solid");
s.setColor("cyan");
s.setPenwidth(2);
s.appendAttributedNode(nodeName);
}
}
/**
* AccessAnalysis aggregates the producer/consumer analysis of a source-CompleteClass via property to target-CompleteClass slot value.
* Distinct accesses are kept separate. RElated accesses such as opposites and deriveds are aggregated by a BasePropertyAnalysis.
*/
private class AccessAnalysis implements Nameable
{
// protected final @NonNull BasePropertyAnalysis basePropertyAnalysis;
protected final @NonNull CompleteClass sourceClass;
protected final @NonNull Property property;
protected final @NonNull CompleteClass targetClass;
// protected final @NonNull String baseName;
protected final @NonNull String name;
private final @NonNull List<@NonNull NamedElement> producers = new ArrayList<>();
private final @NonNull List<@NonNull NavigationCallExp> consumers = new ArrayList<>();
private @Nullable PassRange consumptionPassRange = null;
private @Nullable PassRange productionPassRange = null;
public AccessAnalysis(@NonNull BasePropertyAnalysis basePropertyAnalysis, @NonNull CompleteClass sourceClass, @NonNull Property property, @NonNull CompleteClass targetClass) {
// this.basePropertyAnalysis = basePropertyAnalysis;
this.sourceClass = sourceClass;
this.property = property;
this.targetClass = targetClass;
StringBuilder s = new StringBuilder();
s.append(sourceClass.getName());
s.append("::");
s.append(property.getName());
s.append(" : ");
s.append(property.getType().getName());
// baseName = s.toString();
Property opposite = property.getOpposite();
if (opposite != null) {
s.append("\n");
s.append(targetClass.getName());
s.append("::");
s.append(opposite.getName());
s.append(" : ");
s.append(opposite.getType().getName());
}
this.name = s.toString();
}
public void addConsumer(@NonNull NavigationCallExp consumer) {
assert consumptionPassRange == null;
consumers.add(consumer);
}
public void addProducer(@NonNull NamedElement producer) {
assert productionPassRange == null;
producers.add(producer);
}
public @NonNull PassRange getConsumptionPassRange() {
PassRange consumptionPassRange2 = consumptionPassRange;
if (consumptionPassRange2 == null) {
consumptionPassRange = consumptionPassRange2 = PassRange.create(consumers);
}
return consumptionPassRange2;
}
@Override
public @NonNull String getName() {
return name;
}
public @NonNull PassRange getProductionPassRange() {
PassRange productionPassRange2 = productionPassRange;
if (productionPassRange2 == null) {
productionPassRange = productionPassRange2 = PassRange.create(producers);
}
return productionPassRange2;
}
@Override
public @NonNull String toString() {
return name;
}
}
/**
* ConnectionAnalysis matches the conforming production and consumption analyses that correspond to a connection.
*/
private class ConnectionAnalysis implements Nameable
{
protected final @NonNull BasePropertyAnalysis basePropertyAnalysis;
protected final @NonNull Set<@NonNull AccessAnalysis> producingAnalyses;
protected final @NonNull PassRange productionPassRange;
protected final @NonNull List<@NonNull AccessAnalysis> consumingAnalyses = new ArrayList<>();
private @NonNull PassRange consumptionPassRange = new PassRange();
public ConnectionAnalysis(@NonNull BasePropertyAnalysis basePropertyAnalysis, @NonNull Set<@NonNull AccessAnalysis> producingAnalyses) {
this.basePropertyAnalysis = basePropertyAnalysis;
this.producingAnalyses = producingAnalyses;
PassRange productionPassRange = new PassRange();
for (@NonNull AccessAnalysis producingAnalysis : producingAnalyses) {
productionPassRange = productionPassRange.max(producingAnalysis.getProductionPassRange());
}
this.productionPassRange = productionPassRange;
}
public @NonNull PassRange getConsumptionPassRange() {
return consumptionPassRange;
}
public @NonNull PassRange getProductionPassRange() {
return productionPassRange;
}
public void addConsumingAnalysis(@NonNull AccessAnalysis consumingAnalysis) {
assert !consumingAnalyses.contains(consumingAnalysis);
consumingAnalyses.add(consumingAnalysis);
consumptionPassRange = consumptionPassRange.max(consumingAnalysis.getConsumptionPassRange());
}
public boolean needsNotify() {
return !productionPassRange.precedes(consumptionPassRange);
}
// public boolean needsObserve(@NonNull AccessAnalysis consumingAnalysis) {
// return !productionPassRange.precedes(consumingAnalysis.getConsumptionPassRange());
// }
@Override
public @NonNull String toString() {
return basePropertyAnalysis.toString() + " " + productionPassRange + " " + consumptionPassRange;
}
@Override
public String getName() {
return basePropertyAnalysis.getName();
}
}
protected class ToGraph implements ToGraphHelper
{
protected final @NonNull GraphStringBuilder context;
protected ToGraph(@NonNull GraphStringBuilder context) {
this.context = context;
}
@Override
public @NonNull GraphStringBuilder getGraphStringBuilder() {
return context;
}
protected @NonNull String getLabel(@NonNull GraphNode graphNode) {
String label = "";
if (graphNode instanceof BasePropertyAnalysis) {
BasePropertyAnalysis node = (BasePropertyAnalysis)graphNode;
label = node.getName();
}
return label;
}
@Override
public void setColor(@NonNull GraphElement element) {
throw new UnsupportedOperationException();
}
@Override
public void setHead(@NonNull GraphNode node) {
throw new UnsupportedOperationException();
}
@Override
public void setLabel(@NonNull GraphNode node) {
String label = getLabel(node);
context.setLabel(label);
}
@Override
public void setPenwidth(@NonNull GraphNode node) {
throw new UnsupportedOperationException();
}
@Override
public void setShapeAndStyle(@NonNull GraphNode node) {
throw new UnsupportedOperationException();
}
}
protected final @NonNull EnvironmentFactory environmentFactory;
protected final @NonNull CompilerStep compilerStep;
protected final @NonNull String name;
protected final @NonNull QVTimperativeDomainUsageAnalysis domainUsageAnalysis;
protected final @NonNull Map<@NonNull Property, @NonNull BasePropertyAnalysis> property2basePropertyAnalysis = new HashMap<>();
protected final @NonNull CompleteModel completeModel;
protected final @NonNull Iterable<@NonNull Mapping> mappings;
protected final @NonNull DomainUsage inputUsage;
protected final @NonNull DomainUsage outputUsage;
public QVTiProductionConsumption(@NonNull CompilerStep compilerStep, @NonNull QVTimperativeDomainUsageAnalysis domainUsageAnalysis, @NonNull EntryPoint iEntryPoint) throws IOException {
super(ClassUtil.nonNullState(iEntryPoint.eResource()));
this.environmentFactory = compilerStep.getEnvironmentFactory();
this.compilerStep = compilerStep;
this.name = PivotUtil.getName(iEntryPoint);
this.domainUsageAnalysis = domainUsageAnalysis;
this.completeModel = environmentFactory.getCompleteModel();
this.mappings = QVTimperativeUtil.computeMappingClosure(iEntryPoint);
DomainUsage inputUsage = domainUsageAnalysis.getNoneUsage();
for (@NonNull TypedModel inputTypedModel : QVTimperativeUtil.getInputTypedModels(iEntryPoint)) {
inputUsage = domainUsageAnalysis.union(inputUsage, domainUsageAnalysis.getUsage(inputTypedModel));
}
this.inputUsage = inputUsage;
DomainUsage outputUsage = domainUsageAnalysis.getNoneUsage();
for (@NonNull TypedModel outputTypedModel : QVTimperativeUtil.getOutputTypedModels(iEntryPoint)) {
outputUsage = domainUsageAnalysis.union(outputUsage, domainUsageAnalysis.getUsage(outputTypedModel));
}
this.outputUsage = outputUsage;
}
@Override
public void acceptGraphVisitor(@NonNull ToGraphVisitor2 toGraphVisitor) {
GraphStringBuilder context = toGraphVisitor.getContext();
ToGraph toGraph = new ToGraph(context);
context.setLabel(getGraphName());
context.setColor("black");
context.pushCluster();
Map<@NonNull Mapping, @NonNull MappingNode> mapping2mappingNode = new HashMap<>();
List<@NonNull BasePropertyAnalysis> basePropertyAnalyses = new ArrayList<>(property2basePropertyAnalysis.values());
Collections.sort(basePropertyAnalyses, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull BasePropertyAnalysis basePropertyAnalysis : basePropertyAnalyses) {
basePropertyAnalysis.appendNode(toGraph, mapping2mappingNode);
}
context.popCluster();
}
public void analyze() {
//
// Traverse the model to discover all producers and consumers with a corresponding AccessAnalysis supervised by a BasePropertyAnalysis.
//
for (@NonNull Mapping mapping : mappings) {
for (@NonNull EObject eObject : new TreeIterable(mapping, true)) {
if (eObject instanceof Visitable) {
((Visitable)eObject).accept(this);
}
}
}
//
// Add the oclContainer pseudo-property if used.
//
StandardLibraryHelper standardLibraryHelper = new StandardLibraryHelper(environmentFactory.getStandardLibrary());
Property oclContainerProperty = standardLibraryHelper.getOclContainerProperty();
BasePropertyAnalysis oclContainerPropertyAnalysis = property2basePropertyAnalysis.get(oclContainerProperty);
if (oclContainerPropertyAnalysis != null) {
for (@NonNull BasePropertyAnalysis basePropertyAnalysis : property2basePropertyAnalysis.values()) {
if (basePropertyAnalysis != oclContainerPropertyAnalysis) {
Property baseOppositeProperty = basePropertyAnalysis.baseProperty.getOpposite();
if ((baseOppositeProperty != null) && baseOppositeProperty.isIsComposite()) {
oclContainerPropertyAnalysis.accumulate(basePropertyAnalysis);
}
}
}
}
//
// Build the CommunicationAnalysis instances for each BasePropertyAnalysis.
//
for (@NonNull BasePropertyAnalysis basePropertyAnalysis : property2basePropertyAnalysis.values()) {
basePropertyAnalysis.analyze();
}
}
protected BasePropertyAnalysis getBasePropertyAnalysis(@NonNull Property property) {
Property baseProperty = QVTscheduleUtil.getPrimaryProperty(property);
BasePropertyAnalysis basePropertyAnalysis = property2basePropertyAnalysis.get(baseProperty);
if (basePropertyAnalysis == null) {
basePropertyAnalysis = new BasePropertyAnalysis(baseProperty);
property2basePropertyAnalysis.put(baseProperty, basePropertyAnalysis);
}
return basePropertyAnalysis;
}
protected @NonNull CompleteClass getCompleteClass(@NonNull Type type) {
return completeModel.getCompleteClass(type);
}
protected @NonNull CompleteClass getCompleteClass(@NonNull TypedElement typedElement) {
return getCompleteClass(QVTimperativeUtil.getType(typedElement));
}
@Override
public @NonNull String getGraphName() {
return name;
}
protected boolean isInput(@NonNull DomainUsage usage) {
return (inputUsage.getMask() & usage.getMask()) != 0;
}
@Override
public void toGraph(@NonNull GraphStringBuilder s) {
throw new UnsupportedOperationException();
}
public void validate() {
List<@NonNull BasePropertyAnalysis> sortedBasePropertyAnalyses = new ArrayList<>(property2basePropertyAnalysis.values());
Collections.sort(sortedBasePropertyAnalyses, NameUtil.NAMEABLE_COMPARATOR);
StringBuilder s = SUMMARY.isActive() ? new StringBuilder() : null;
for (@NonNull BasePropertyAnalysis basePropertyAnalysis : sortedBasePropertyAnalyses) {
basePropertyAnalysis.validate(s);
}
if (s != null) {
SUMMARY.println(s.toString());
}
}
@Override
public @Nullable Object visiting(@NonNull Visitable visitable) {
throw new UnsupportedOperationException("Unimplemented " + getClass().getName() + " for " + visitable.eClass().getName());
}
@Override
public @Nullable Object visitElement(@NonNull Element object) {
return null;
}
@Override
public @Nullable Object visitGuardParameter(@NonNull GuardParameter guardParameter) {
Property successProperty = guardParameter.getSuccessProperty();
if (successProperty != null) {
CompleteClass sourceClass = getCompleteClass(QVTimperativeUtil.getType(guardParameter));
CompleteClass targetClass = getCompleteClass(successProperty);
BasePropertyAnalysis basePropertyAnalysis = getBasePropertyAnalysis(successProperty);
basePropertyAnalysis.addProducer(guardParameter, sourceClass, successProperty, targetClass);
}
return null;
}
@Override
public @Nullable Object visitNavigationCallExp(@NonNull NavigationCallExp navigationCallExp) {
Mapping mapping = QVTimperativeUtil.basicGetContainingMapping(navigationCallExp);
if (mapping != null) {
OCLExpression ownedSource = QVTimperativeUtil.getOwnedSource(navigationCallExp);
DomainUsage usage = domainUsageAnalysis.getUsage(navigationCallExp instanceof OppositePropertyCallExp ? navigationCallExp : ownedSource);
if (!isInput(usage) && !usage.isPrimitive()) { // Skip endogenously confusing input
Property getProperty = QVTimperativeUtil.getReferredProperty(navigationCallExp);
BasePropertyAnalysis basePropertyAnalysis = getBasePropertyAnalysis(getProperty);
CompleteClass sourceClass = getCompleteClass(ownedSource);
CompleteClass targetClass;
EObject eContainer = navigationCallExp.eContainer();
if (eContainer instanceof DeclareStatement) {
targetClass = getCompleteClass(QVTimperativeUtil.getType((DeclareStatement)eContainer));
}
else {
targetClass = getCompleteClass(navigationCallExp);
}
if (getProperty.isIsMany()) {
CollectionType collectionType = (CollectionType)targetClass.getPrimaryClass();
targetClass = getCompleteClass(PivotUtil.getElementType(collectionType));
}
basePropertyAnalysis.addConsumer(navigationCallExp, sourceClass, getProperty, targetClass);
}
}
return null;
}
@Override
public @Nullable Object visitNewStatement(@NonNull NewStatement newStatement) {
return null;
}
@Override
public @Nullable Object visitNewStatementPart(@NonNull NewStatementPart newStatementPart) {
NewStatement newStatement = QVTimperativeUtil.getOwningNewStatement(newStatementPart);
Property setProperty = QVTimperativeUtil.getReferredProperty(newStatementPart);
BasePropertyAnalysis basePropertyAnalysis = getBasePropertyAnalysis(setProperty);
CompleteClass sourceClass = getCompleteClass(QVTimperativeUtil.getType(newStatement));
CompleteClass targetClass = getCompleteClass(QVTimperativeUtil.getOwnedExpression(newStatementPart));
// if (!newStatementPart.isIsPartial() && setProperty.isIsMany()) {
// CollectionType collectionType = (CollectionType)targetClass.getPrimaryClass();
// targetClass = getCompleteClass(PivotUtil.getElementType(collectionType));
// }
basePropertyAnalysis.addProducer(newStatementPart, sourceClass, setProperty, targetClass);
return null;
}
@Override
public @Nullable Object visitSetStatement(@NonNull SetStatement setStatement) {
Property setProperty = QVTimperativeUtil.getTargetProperty(setStatement);
BasePropertyAnalysis basePropertyAnalysis = getBasePropertyAnalysis(setProperty);
CompleteClass sourceClass = getCompleteClass(QVTimperativeUtil.getTargetVariable(setStatement));
CompleteClass targetClass = getCompleteClass(QVTimperativeUtil.getOwnedExpression(setStatement));
if (!setStatement.isIsPartial() && setProperty.isIsMany()) {
CollectionType collectionType = (CollectionType)targetClass.getPrimaryClass();
targetClass = getCompleteClass(PivotUtil.getElementType(collectionType));
}
basePropertyAnalysis.addProducer(setStatement, sourceClass, setProperty, targetClass);
return null;
}
}