blob: 075c9f7f3d2eb8fbb126b7f366fe155cb0d4b9f3 [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.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.CollectionType;
import org.eclipse.ocl.pivot.CompleteEnvironment;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.qvtd.compiler.internal.qvtp2qvts.analysis.ClassDatumAnalysis;
import org.eclipse.qvtd.compiler.internal.utilities.SymbolNameBuilder;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.graphs.GraphStringBuilder;
import org.eclipse.qvtd.pivot.qvtimperative.utilities.QVTimperativeUtil;
public class RootCompositionRegion extends AbstractRegion
{
/**
* The null node that is the 'container' of all root model elements.
*/
private /*@LazyNonNull*/ Node nullNode = null;
/**
* The introducer node for each consumed ClassDatumAnalysis and for each known container property where the containing property is known.
* The null containing property is used for introducer nodes required to be at the root.
*/
private final @NonNull Map<@NonNull ClassDatumAnalysis, @NonNull Map<@Nullable Property, @NonNull Node>> classDatumAnalysis2property2node = new HashMap<>();
/**
* The introducer node for each consumed ClassDatumAnalysis and for each known containing type where the containing property is just oclContainer.
* The null type is used when no containing property or type is known.
*/
private final @NonNull Map<@NonNull ClassDatumAnalysis, @NonNull Map<@Nullable ClassDatumAnalysis, @NonNull Node>> classDatumAnalysis2type2node = new HashMap<>();
protected RootCompositionRegion(@NonNull MultiRegion multiRegion) {
super(multiRegion);
}
@Override
public <R> R accept(@NonNull Visitor<R> visitor) {
return visitor.visitRootCompositionRegion(this);
}
@Override
protected @NonNull SymbolNameBuilder computeSymbolName() {
SymbolNameBuilder s = new SymbolNameBuilder();
s.appendString(QVTimperativeUtil.ROOT_MAPPING_NAME);
return s;
}
@Override
public void createIncomingConnections() {}
public @NonNull Node getIntroducerNode(@NonNull Node consumerNode) {
//
// Identify the containment pattern.
//
NavigableEdge containerEdge = null;
Property parent2childProperty = null;
ClassDatumAnalysis containingClassDatumAnalysis = null;
SchedulerConstants scheduler = getSchedulerConstants();
for (@NonNull NavigableEdge edge : consumerNode.getNavigationEdges()) {
Property property = edge.getProperty().getOpposite();
if ((property != null) && property.isIsComposite() && !property.isIsRequired()) {
containerEdge = edge;
parent2childProperty = property;
if (property == scheduler.getStandardLibraryHelper().getOclContainerProperty()) {
containingClassDatumAnalysis = edge.getSource().getClassDatumAnalysis();
}
break;
}
}
CompleteEnvironment completeEnvironment = scheduler.getEnvironmentFactory().getCompleteEnvironment();
ClassDatumAnalysis consumedClassDatumAnalysis = /*getCastTarget(consumerNode)*/consumerNode.getClassDatumAnalysis();
org.eclipse.ocl.pivot.Class elementType = consumedClassDatumAnalysis.getCompleteClass().getPrimaryClass();
TypedModel typedModel = consumedClassDatumAnalysis.getTypedModel();
CollectionType childCollectionType = completeEnvironment.getSetType(elementType, true, null, null);
ClassDatumAnalysis childrenClassDatumAnalysis = scheduler.getClassDatumAnalysis(childCollectionType, typedModel);
//
// Create / re-use the appropriate containment pattern.
//
Node introducedNode;
if (parent2childProperty == null) { // No known containment, owned by null type
Map<@Nullable ClassDatumAnalysis, @NonNull Node> type2node = classDatumAnalysis2type2node.get(consumedClassDatumAnalysis);
if (type2node == null) {
type2node = new HashMap<>();
classDatumAnalysis2type2node.put(consumedClassDatumAnalysis, type2node);
}
introducedNode = type2node.get(null);
if (introducedNode == null) {
introducedNode = RegionUtil.createComposingNode(this, "«" + elementType.getName() + "»", childrenClassDatumAnalysis);
type2node.put(null, introducedNode);
}
}
else if ((containerEdge != null) && containerEdge.getTarget().isExplicitNull()) { // At root, owned by null property
Map<@Nullable Property, @NonNull Node> property2node = classDatumAnalysis2property2node.get(consumedClassDatumAnalysis);
if (property2node == null) {
property2node = new HashMap<>();
classDatumAnalysis2property2node.put(consumedClassDatumAnalysis, property2node);
}
introducedNode = property2node.get(null);
if (introducedNode == null) {
introducedNode = RegionUtil.createComposingNode(this, "«" + elementType.getName() + "-null»", childrenClassDatumAnalysis);
property2node.put(null, introducedNode);
RegionUtil.createNavigationEdge(getNullNode(), parent2childProperty, introducedNode);
}
}
else if (containingClassDatumAnalysis != null) { // Non-root oclContainer ownership
Map<@Nullable ClassDatumAnalysis, @NonNull Node> type2node = classDatumAnalysis2type2node.get(consumedClassDatumAnalysis);
if (type2node == null) {
type2node = new HashMap<>();
classDatumAnalysis2type2node.put(consumedClassDatumAnalysis, type2node);
}
introducedNode = type2node.get(containingClassDatumAnalysis);
if (introducedNode == null) {
introducedNode = RegionUtil.createComposingNode(this, "«" + elementType.getName() + "-oclContents»", childrenClassDatumAnalysis);
type2node.put(containingClassDatumAnalysis, introducedNode);
Node containerNode = RegionUtil.createComposingNode(this, "«" + containingClassDatumAnalysis.getCompleteClass().getName() + "-oclContainer»", containingClassDatumAnalysis);
RegionUtil.createNavigationEdge(containerNode, parent2childProperty, introducedNode);
}
}
else { // Knonw distinctive containment
Map<@Nullable Property, @NonNull Node> property2node = classDatumAnalysis2property2node.get(consumedClassDatumAnalysis);
if (property2node == null) {
property2node = new HashMap<>();
classDatumAnalysis2property2node.put(consumedClassDatumAnalysis, property2node);
}
introducedNode = property2node.get(parent2childProperty);
if (introducedNode == null) {
introducedNode = RegionUtil.createComposingNode(this, "«" + elementType.getName() + "-" + parent2childProperty.getName() + "»", childrenClassDatumAnalysis);
property2node.put(parent2childProperty, introducedNode);
org.eclipse.ocl.pivot.Class owningClass = parent2childProperty.getOwningClass();
assert owningClass != null;
containingClassDatumAnalysis = scheduler.getClassDatumAnalysis(owningClass, typedModel);
Node containerNode = RegionUtil.createComposingNode(this, "«" + owningClass.getName() + "-" + parent2childProperty.getName() + "»", containingClassDatumAnalysis);
RegionUtil.createNavigationEdge(containerNode, parent2childProperty, introducedNode);
}
}
return introducedNode;
}
@Override
public @NonNull String getName() {
return QVTimperativeUtil.ROOT_MAPPING_NAME;
}
@Override
public @NonNull List<@NonNull Node> getHeadNodes() {
return SchedulerConstants.EMPTY_NODE_LIST;
}
private @NonNull Node getNullNode() {
Node nullNode2 = nullNode;
if (nullNode2 == null) {
nullNode = nullNode2 = RegionUtil.createNullNode(this, true, null);
}
return nullNode2;
}
@Override
public boolean isRootCompositionRegion() {
return true;
}
@Override
public void toGraph(@NonNull GraphStringBuilder s) {
s.setLabel(getName());
s.setColor("lightblue");
s.setPenwidth(Role.LINE_WIDTH);
s.pushCluster();
for (@NonNull Node node : getNodes()) {
s.appendNode(node);
}
for (@NonNull Edge edge : getEdges()) {
s.appendEdge(edge.getSource(), edge, edge.getTarget());
}
s.popCluster();
}
}