blob: bccbca6e827b9b339a09713ed132a1f5bdb0e6eb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 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:
* Adolfo Sanchez-Barbudo Herrera - initial API and implementation
* E.D.Willink - use Complete model
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvtm2qvts;
import java.util.HashMap;
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.qvts2qvts.ClassDatumAnalysis;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.LoadingRegion;
/**
* A RootMappingAnalysis provides an analysis of what is introduced by the root mapping.
*/
public class RootMappingAnalysis
{
protected final @NonNull LoadingRegion loadingRegion;
/**
* 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<>();
public RootMappingAnalysis(@NonNull LoadingRegion loadingRegion) {
this.loadingRegion = loadingRegion;
}
public @NonNull Node getIntroducerNode(@NonNull Node consumerNode) {
ScheduleManager scheduleManager = RegionUtil.getScheduleManager(loadingRegion);
//
// Identify the containment pattern.
//
NavigableEdge containerEdge = null;
Property parent2childProperty = null;
ClassDatumAnalysis containingClassDatumAnalysis = null;
for (@NonNull NavigableEdge edge : consumerNode.getNavigationEdges()) {
Property property = edge.getProperty().getOpposite();
if ((property != null) && property.isIsComposite() && !property.isIsRequired()) {
containerEdge = edge;
parent2childProperty = property;
if (property == scheduleManager.getStandardLibraryHelper().getOclContainerProperty()) {
containingClassDatumAnalysis = RegionUtil.getClassDatumAnalysis(edge.getEdgeSource());
}
break;
}
}
CompleteEnvironment completeEnvironment = scheduleManager.getEnvironmentFactory().getCompleteEnvironment();
ClassDatumAnalysis consumedClassDatumAnalysis = /*getCastTarget(consumerNode)*/RegionUtil.getClassDatumAnalysis(consumerNode);
org.eclipse.ocl.pivot.Class elementType = consumedClassDatumAnalysis.getClassDatum().getCompleteClass().getPrimaryClass();
TypedModel typedModel = RegionUtil.getTypedModel(consumedClassDatumAnalysis);
CollectionType childCollectionType = completeEnvironment.getSetType(elementType, true, null, null);
ClassDatumAnalysis childrenClassDatumAnalysis = scheduleManager.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(loadingRegion, "«" + elementType.getName() + "»", childrenClassDatumAnalysis);
type2node.put(null, introducedNode);
}
}
else if ((containerEdge != null) && containerEdge.getEdgeTarget().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(loadingRegion, "«" + elementType.getName() + "-null»", childrenClassDatumAnalysis);
property2node.put(null, introducedNode);
RegionUtil.createNavigationEdge(getNullNode(), parent2childProperty, introducedNode, false);
}
}
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(loadingRegion, "«" + elementType.getName() + "-oclContents»", childrenClassDatumAnalysis);
type2node.put(containingClassDatumAnalysis, introducedNode);
Node containerNode = RegionUtil.createComposingNode(loadingRegion, "«" + containingClassDatumAnalysis.getClassDatum().getCompleteClass().getName() + "-oclContainer»", containingClassDatumAnalysis);
RegionUtil.createNavigationEdge(containerNode, parent2childProperty, introducedNode, false);
}
}
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(loadingRegion, "«" + elementType.getName() + "-" + parent2childProperty.getName() + "»", childrenClassDatumAnalysis);
property2node.put(parent2childProperty, introducedNode);
org.eclipse.ocl.pivot.Class owningClass = parent2childProperty.getOwningClass();
assert owningClass != null;
containingClassDatumAnalysis = scheduleManager.getClassDatumAnalysis(owningClass, typedModel);
Node containerNode = RegionUtil.createComposingNode(loadingRegion, "«" + owningClass.getName() + "-" + parent2childProperty.getName() + "»", containingClassDatumAnalysis);
RegionUtil.createNavigationEdge(containerNode, parent2childProperty, introducedNode, false);
}
}
return introducedNode;
}
protected @NonNull Node getNullNode() {
Node nullNode2 = nullNode;
if (nullNode2 == null) {
nullNode = nullNode2 = RegionUtil.createNullNode(loadingRegion, true, null);
}
return nullNode2;
}
}