blob: 2931c5c7ad21918972391a8b7e85a25367e029bd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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:
* Horacio Hoyos - initial research
* E.D.Willink - initial API and implementation based on MtcBroker
******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvtu2qvtm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
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.CompleteClass;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.OCLExpression;
import org.eclipse.ocl.pivot.Property;
import org.eclipse.ocl.pivot.Type;
import org.eclipse.ocl.pivot.Variable;
import org.eclipse.ocl.pivot.VariableDeclaration;
import org.eclipse.ocl.pivot.VariableExp;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.pivot.utilities.EnvironmentFactory;
import org.eclipse.ocl.pivot.utilities.PivotUtil;
import org.eclipse.qvtd.compiler.internal.common.AbstractQVTc2QVTc;
import org.eclipse.qvtd.pivot.qvtbase.Domain;
import org.eclipse.qvtd.pivot.qvtbase.Predicate;
import org.eclipse.qvtd.pivot.qvtbase.QVTbaseFactory;
import org.eclipse.qvtd.pivot.qvtbase.Rule;
import org.eclipse.qvtd.pivot.qvtbase.Transformation;
import org.eclipse.qvtd.pivot.qvtbase.TypedModel;
import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil;
import org.eclipse.qvtd.pivot.qvtcore.Area;
import org.eclipse.qvtd.pivot.qvtcore.Assignment;
import org.eclipse.qvtd.pivot.qvtcore.BottomPattern;
import org.eclipse.qvtd.pivot.qvtcore.CoreDomain;
import org.eclipse.qvtd.pivot.qvtcore.CoreModel;
import org.eclipse.qvtd.pivot.qvtcore.CorePattern;
import org.eclipse.qvtd.pivot.qvtcore.GuardPattern;
import org.eclipse.qvtd.pivot.qvtcore.Mapping;
import org.eclipse.qvtd.pivot.qvtcore.NavigationAssignment;
import org.eclipse.qvtd.pivot.qvtcore.QVTcoreFactory;
import org.eclipse.qvtd.pivot.qvtcore.RealizedVariable;
import org.eclipse.qvtd.pivot.qvtcore.VariableAssignment;
import org.eclipse.qvtd.pivot.qvtcore.utilities.QVTcoreUtil;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.QVTscheduleConstants;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.SymbolNameBuilder;
import org.eclipse.qvtd.pivot.qvtschedule.utilities.SymbolNameReservation;
import com.google.common.collect.Iterables;
/**
* QVTu2QVTm simplifies a QVTu transformation
* - flattening nested mappings
* - flattening mapping refinements
*/
public class QVTu2QVTm extends AbstractQVTc2QVTc
{
protected static class CreateVisitor extends AbstractCreateVisitor<@NonNull QVTu2QVTm>
{
public CreateVisitor(@NonNull QVTu2QVTm context) {
super(context);
}
public @NonNull QVTu2QVTm getContext() {
return context;
}
@Override
protected void doRules(@NonNull Transformation tIn, @NonNull Transformation tOut) {
List<@NonNull Mapping> requiredMappings = new ArrayList<@NonNull Mapping>();
for (@NonNull Rule rule : ClassUtil.nullFree(tIn.getRule())) {
if (rule instanceof Mapping) {
requiredMappings.add((Mapping)rule);
gatherRequiredMappings((Mapping)rule, requiredMappings);
}
}
createAll(requiredMappings, tOut.getRule());
}
private void gatherRequiredMappings(@NonNull Mapping mapping, @NonNull List<@NonNull Mapping> requiredMappings) {
for (@NonNull Mapping localMapping : ClassUtil.nullFree(mapping.getLocal())) {
if (!isFoldable(localMapping)) {
requiredMappings.add(localMapping);
}
gatherRequiredMappings(localMapping, requiredMappings);
}
}
@Override
public @NonNull CoreModel visitCoreModel(@NonNull CoreModel mIn) {
CoreModel mOut = super.visitCoreModel(mIn);
mOut.setExternalURI(mIn.getExternalURI().replace(".qvtu.qvtcas", ".qvtm.qvtcas"));
return mOut;
}
@Override
public @Nullable Mapping visitMapping(@NonNull Mapping mIn) {
assert !isFoldable(mIn);
// if (/*isAbstract(mIn) ||*/ isFoldable(mIn)) {
// return null;
// }
if (!context.isRequired(mIn)) {
return null;
}
Mapping mMapping = QVTcoreFactory.eINSTANCE.createMapping();
context.pushScope(mMapping);
try {
MergedMapping mergedMapping = new MergedMapping(this, mIn);
if (!mergedMapping.analyze()) {
return null;
}
mergedMapping.synthesize(mMapping);
return mMapping;
}
finally {
context.popScope();
}
}
@Override
public @NonNull TypedModel visitTypedModel(@NonNull TypedModel tmIn) {
TypedModel tmOut = super.visitTypedModel(tmIn);
if (tmOut.getName() == null) {
tmOut.setName(QVTscheduleConstants.MIDDLE_DOMAIN_NAME); // FIXME move to super / earlier
}
return tmOut;
}
}
protected abstract static class MergedArea
{
private static final boolean ASSIGNMENT = true;
private static final boolean PREDICATE = false;
protected final @NonNull CreateVisitor createVisitor;
private final @NonNull List<@NonNull Predicate> guardPredicates = new ArrayList<@NonNull Predicate>();
private final @NonNull List<@NonNull Predicate> bottomPredicates = new ArrayList<@NonNull Predicate>();
private @Nullable LinkedHashMap<@NonNull String, @NonNull MergedVariable> variableName2mergedVariables = null;
private @Nullable LinkedHashMap<@NonNull String, @NonNull List<@NonNull NavigationAssignment>> variableName_property2navigationAssignments = null;
private @Nullable List<@NonNull NavigationAssignment> navigationAssignmentsAsPredicates = null;
protected MergedArea(@NonNull CreateVisitor createVisitor) {
this.createVisitor = createVisitor;
}
protected boolean analyze() {
Area thisArea = getArea(getMapping());
if (thisArea != null) {
GuardPattern thisGuardPattern = QVTcoreUtil.getGuardPattern(thisArea);
Iterables.addAll(guardPredicates, QVTbaseUtil.getPredicates(thisGuardPattern));
gatherVariables(QVTcoreUtil.getOwnedVariables(thisGuardPattern), MergedVariable.GUARD);
BottomPattern thisBottomPattern = QVTcoreUtil.getBottomPattern(thisArea);
gatherVariables(QVTcoreUtil.getOwnedVariables(thisBottomPattern), MergedVariable.BOTTOM);
gatherAssignments(QVTcoreUtil.getOwnedAssignments(thisBottomPattern), ASSIGNMENT);
Iterables.addAll(bottomPredicates, QVTbaseUtil.getPredicates(thisBottomPattern));
gatherVariables(QVTcoreUtil.getOwnedRealizedVariables(thisBottomPattern), MergedVariable.BOTTOM);
}
for (@NonNull Area childArea : getChildAreas()) {
GuardPattern childGuardPattern = QVTcoreUtil.getGuardPattern(childArea);
Iterables.addAll(guardPredicates, QVTbaseUtil.getPredicates(childGuardPattern));
gatherVariables(QVTcoreUtil.getOwnedVariables(childGuardPattern), MergedVariable.GUARD);
BottomPattern childBottomPattern = QVTcoreUtil.getBottomPattern(childArea);
gatherVariables(QVTcoreUtil.getOwnedVariables(childBottomPattern), MergedVariable.BOTTOM);
gatherAssignments(QVTcoreUtil.getOwnedAssignments(childBottomPattern), ASSIGNMENT);
Iterables.addAll(bottomPredicates, QVTbaseUtil.getPredicates(childBottomPattern));
gatherVariables(QVTcoreUtil.getOwnedRealizedVariables(childBottomPattern), MergedVariable.BOTTOM);
}
for (@NonNull Area parentArea : getParentAreas()) {
assert (parentArea != thisArea);
GuardPattern parentGuardPattern = QVTcoreUtil.getGuardPattern(parentArea);
Iterables.addAll(guardPredicates, QVTbaseUtil.getPredicates(parentGuardPattern));
gatherVariables(QVTcoreUtil.getOwnedVariables(parentGuardPattern), MergedVariable.GUARD);
BottomPattern parentBottomPattern = QVTcoreUtil.getBottomPattern(parentArea);
gatherVariables(QVTcoreUtil.getOwnedVariables(parentBottomPattern), MergedVariable.GUARD); // Hoist
gatherAssignments(QVTcoreUtil.getOwnedAssignments(parentBottomPattern), PREDICATE);
Iterables.addAll(guardPredicates, QVTbaseUtil.getPredicates(parentBottomPattern)); // Hoist
gatherVariables(QVTcoreUtil.getOwnedRealizedVariables(parentBottomPattern), MergedVariable.GUARD); // Hoist
}
for (@NonNull Area siblingArea : getSiblingAreas()) {
if (siblingArea != thisArea) {
GuardPattern siblingGuardPattern = QVTcoreUtil.getGuardPattern(siblingArea);
Iterables.addAll(guardPredicates, QVTbaseUtil.getPredicates(siblingGuardPattern));
gatherVariables(QVTcoreUtil.getOwnedVariables(siblingGuardPattern), MergedVariable.GUARD);
BottomPattern siblingBottomPattern = QVTcoreUtil.getBottomPattern(siblingArea);
gatherVariables(QVTcoreUtil.getOwnedVariables(siblingBottomPattern), MergedVariable.BOTTOM); // FIXME legacy compatibility
gatherAssignments(QVTcoreUtil.getOwnedAssignments(siblingBottomPattern), ASSIGNMENT);
Iterables.addAll(bottomPredicates, QVTbaseUtil.getPredicates(siblingBottomPattern)); // FIXME legacy compatibility
gatherVariables(QVTcoreUtil.getOwnedRealizedVariables(siblingBottomPattern), MergedVariable.BOTTOM);
}
}
return true;
}
protected @Nullable MergedVariable basicGetMergedVariable(@NonNull String name) {
LinkedHashMap<@NonNull String, @NonNull MergedVariable> variableName2mergedVariables2 = variableName2mergedVariables;
if (variableName2mergedVariables2 == null) {
return null;
}
return variableName2mergedVariables2.get(name);
}
private void gatherAssignments(@NonNull Iterable</*@NonNull*/ Assignment> aIns, boolean asAssignment) {
for (Assignment aIn : aIns) {
if (aIn instanceof VariableAssignment) {
VariableAssignment vaIn = (VariableAssignment)aIn;
Variable vIn = vaIn.getTargetVariable();
String name = vIn.getName();
assert name != null;
MergedVariable mergedVariable = getMergedVariable(name);
mergedVariable.addAssignment(vaIn);
}
else if (aIn instanceof NavigationAssignment) {
NavigationAssignment paIn = (NavigationAssignment)aIn;
if (!asAssignment) {
List<@NonNull NavigationAssignment> navigationAssignmentsAsPredicates2 = navigationAssignmentsAsPredicates;
if (navigationAssignmentsAsPredicates2 == null) {
navigationAssignmentsAsPredicates2 = navigationAssignmentsAsPredicates = new ArrayList<@NonNull NavigationAssignment>();
}
navigationAssignmentsAsPredicates2.add(paIn);
}
else {
OCLExpression eIn = paIn.getSlotExpression();
Property targetProperty = QVTcoreUtil.getTargetProperty(paIn);
if (eIn instanceof VariableExp) {
VariableDeclaration vIn = ((VariableExp)eIn).getReferredVariable();
String key = vIn.getName() + "%" + targetProperty.toString();
assert key != null;
LinkedHashMap<@NonNull String, @NonNull List<@NonNull NavigationAssignment>> variableName_property2navigationAssignments2 = variableName_property2navigationAssignments;
if (variableName_property2navigationAssignments2 == null) {
variableName_property2navigationAssignments2 = variableName_property2navigationAssignments = new LinkedHashMap<@NonNull String, @NonNull List<@NonNull NavigationAssignment>>();
}
List<@NonNull NavigationAssignment> navigationAssignments = variableName_property2navigationAssignments2.get(key);
if (navigationAssignments == null) {
navigationAssignments = new ArrayList<@NonNull NavigationAssignment>();
variableName_property2navigationAssignments2.put(key, navigationAssignments);
}
navigationAssignments.add(paIn);
}
}
}
}
}
private <T extends Variable> void gatherVariables(@NonNull Iterable</*@NonNull*/ ? extends T> vIns, boolean isGuard) {
for (T vIn : vIns) {
if (vIn != null) {
String name = vIn.getName();
assert name != null;
MergedVariable mergedVariable = getMergedVariable(name);
mergedVariable.addVariable(vIn, isGuard);
}
}
}
protected abstract @Nullable Area getArea(@NonNull Mapping uMapping);
protected abstract @NonNull Iterable<? extends @NonNull Area> getChildAreas();
public @NonNull CreateVisitor getCreateVisitor() {
return createVisitor;
}
protected abstract @NonNull Mapping getMapping();
protected @NonNull MergedVariable getMergedVariable(@NonNull String name) {
LinkedHashMap<@NonNull String, @NonNull MergedVariable> variableName2mergedVariables2 = variableName2mergedVariables;
if (variableName2mergedVariables2 == null) {
variableName2mergedVariables = variableName2mergedVariables2 = new LinkedHashMap<@NonNull String, @NonNull MergedVariable>();
}
MergedVariable mergedVariable = variableName2mergedVariables2.get(name);
if (mergedVariable == null) {
mergedVariable = new MergedVariable(this, name);
variableName2mergedVariables2.put(name, mergedVariable);
}
return mergedVariable;
}
protected abstract @NonNull Iterable<? extends @NonNull Area> getParentAreas();
protected abstract @NonNull Iterable<? extends @NonNull Area> getSiblingAreas();
protected boolean isEnforced() {
return false;
}
protected void synthesize(@NonNull Mapping mMapping, @NonNull Area mArea) {
GuardPattern mGuardPattern = QVTcoreFactory.eINSTANCE.createGuardPattern();
mArea.setGuardPattern(mGuardPattern);
createVisitor.createAll(guardPredicates, mGuardPattern.getPredicate());
if (navigationAssignmentsAsPredicates != null) {
for (@NonNull NavigationAssignment paIn : navigationAssignmentsAsPredicates) {
@NonNull Predicate pOut = QVTbaseFactory.eINSTANCE.createPredicate();
createVisitor.getContext().addTrace(paIn, pOut);
// The condition expression is copied during update once replacement variables exist.
createVisitor.createAll(paIn.getOwnedComments(), pOut.getOwnedComments());
mGuardPattern.getPredicate().add(pOut);
}
}
//
BottomPattern mBottomPattern = QVTcoreFactory.eINSTANCE.createBottomPattern();
mArea.setBottomPattern(mBottomPattern);
createVisitor.createAll(bottomPredicates, mBottomPattern.getPredicate());
synthesizeNavigationAssignments(QVTcoreUtil.Internal.getOwnedAssignmentsList(mBottomPattern));
if (variableName2mergedVariables != null) {
for (@NonNull MergedVariable mergedVariable : variableName2mergedVariables.values()) { // FIXME Change to alphabetical sort
mergedVariable.synthesize(mMapping, mArea);
}
}
//
QVTu2QVTm qvtu2qvtm = createVisitor.getContext();
for (@NonNull Area uArea : Iterables.concat(getSiblingAreas(), getParentAreas(), getChildAreas())) {
qvtu2qvtm.addTrace(uArea, mArea);
if (uArea == getArea(getMapping())) { // FIXME Legacy compatibility
createVisitor.createAll(uArea.getOwnedComments(), mArea.getOwnedComments());
}
//
GuardPattern uGuardPattern = QVTcoreUtil.getGuardPattern(uArea);
qvtu2qvtm.addTrace(uGuardPattern, mGuardPattern);
createVisitor.createAll(uGuardPattern.getOwnedComments(), mGuardPattern.getOwnedComments());
//
BottomPattern uBottomPattern = QVTcoreUtil.getBottomPattern(uArea);
qvtu2qvtm.addTrace(uBottomPattern, mBottomPattern);
createVisitor.createAll(uBottomPattern.getOwnedComments(), mArea.getOwnedComments());
}
}
private void synthesizeNavigationAssignment(@NonNull Iterable<@NonNull NavigationAssignment> navigationAssignments, @NonNull List<Assignment> uAssignments) {
for (@NonNull NavigationAssignment navigationAssignment : navigationAssignments) {
uAssignments.add(createVisitor.create(navigationAssignment)); // FIXME merge
// break;
}
}
private void synthesizeNavigationAssignments(@NonNull List</*@NonNull*/ Assignment> uAssignments) {
LinkedHashMap<@NonNull String, @NonNull List<@NonNull NavigationAssignment>> variableName_property2navigationAssignments2 = variableName_property2navigationAssignments;
if (variableName_property2navigationAssignments2 != null) {
for (@NonNull Iterable<@NonNull NavigationAssignment> navigationAssignments : variableName_property2navigationAssignments2.values()) { // FIXME Change to alphabetical sort
synthesizeNavigationAssignment(navigationAssignments, uAssignments);
}
}
}
}
protected static class MergedDomain extends MergedArea
{
/**
* The merged mapping to which this merged domain contributes.
*/
protected final @NonNull MergedMapping mergedMapping;
/**
* The typed model that identifies this domain.
*/
protected final @NonNull TypedModel uTypedModel;
/**
* A domain, not necessarily from the root or local mapping that has the check/enfoprce flags.
*/
protected final @NonNull CoreDomain uExampleDomain;
/**
* The local domain for the local mapping., which may be null if there is no local domain.
*/
protected final @Nullable CoreDomain uLocalDomain;
/**
* All local QVTi CoreDomains that merge up into the QVTm CoreDomain.
*/
private final @NonNull List<@NonNull CoreDomain> childDomains = new ArrayList<@NonNull CoreDomain>();
/**
* All context QVTi CoreDomains that predicate the QVTm CoreDomain.
*/
private final @NonNull List<@NonNull CoreDomain> parentDomains = new ArrayList<@NonNull CoreDomain>();
/**
* All refined QVTi CoreDomains (including this domain) that merge into the QVTm CoreDomain.
*/
private final @NonNull List<@NonNull CoreDomain> siblingDomains = new ArrayList<@NonNull CoreDomain>();
public MergedDomain(@NonNull CreateVisitor createVisitor, @NonNull MergedMapping mergedMapping, @NonNull TypedModel uTypedModel, @NonNull CoreDomain uExampleDomain) {
super(createVisitor);
this.mergedMapping = mergedMapping;
this.uTypedModel = uTypedModel;
this.uExampleDomain = uExampleDomain;
this.uLocalDomain = QVTcoreUtil.getDomain(mergedMapping.uMapping, uTypedModel);
}
@Override
public boolean analyze() {
for (@NonNull Mapping childMapping : mergedMapping.getChildAreas()) {
CoreDomain childDomain = QVTcoreUtil.getDomain(childMapping, uTypedModel);
if (childDomain != null) {
childDomains.add(childDomain);
}
}
for (@NonNull Mapping parentMapping : mergedMapping.getParentAreas()) {
CoreDomain parentDomain = QVTcoreUtil.getDomain(parentMapping, uTypedModel);
if (parentDomain != null) {
parentDomains.add(parentDomain);
}
}
for (@NonNull Mapping siblingMapping : mergedMapping.getSiblingAreas()) {
CoreDomain siblingDomain = QVTcoreUtil.getDomain(siblingMapping, uTypedModel);
if (siblingDomain != null) {
siblingDomains.add(siblingDomain);
}
}
return super.analyze();
}
// @Override
// protected @NonNull CoreDomain zzgetArea() {
// return uNonLocalDomain;
// }
@Override
protected @Nullable CoreDomain getArea(@NonNull Mapping uMapping) {
return QVTcoreUtil.getDomain(uMapping, uTypedModel);
}
@Override
protected @NonNull Iterable<@NonNull CoreDomain> getChildAreas() {
return childDomains;
}
@Override
protected @NonNull Mapping getMapping() {
return mergedMapping.uMapping;
}
@Override
protected @NonNull Iterable<@NonNull CoreDomain> getParentAreas() {
return parentDomains;
}
@Override
protected @NonNull Iterable<@NonNull CoreDomain> getSiblingAreas() {
return siblingDomains;
}
@Override
protected boolean isEnforced() {
return uExampleDomain.isIsEnforceable(); // FIXME all domains ??
}
public @NonNull CoreDomain synthesize(@NonNull Mapping mMapping) {
CoreDomain mDomain = QVTcoreFactory.eINSTANCE.createCoreDomain();
// mDomain.setTypedModel(uTypedModel);
mDomain.setName(uTypedModel.getName());
boolean isCheckable = uExampleDomain.isIsCheckable();
boolean isEnforceable = uExampleDomain.isIsEnforceable();
for (@NonNull CoreDomain domain : parentDomains) {
if (domain.isIsCheckable()) {
isCheckable = true;
}
if (domain.isIsEnforceable()) {
isEnforceable = true;
}
}
for (@NonNull CoreDomain domain : siblingDomains) {
if (domain.isIsCheckable()) {
isCheckable = true;
}
if (domain.isIsEnforceable()) {
isEnforceable = true;
}
}
mDomain.setIsCheckable(isCheckable);
mDomain.setIsEnforceable(isEnforceable);
synthesize(mMapping, mDomain);
return mDomain;
}
@Override
public String toString() {
return uExampleDomain.toString();
}
}
protected static class MergedMapping extends MergedArea
{
/**
* The QVTu Mapping into which contributions may merge.
*/
protected final @NonNull Mapping uMapping;
/**
* All local QVTi Mappings that merge up into the QVTm Mappings.
*/
private final @NonNull List<@NonNull Mapping> childMappings = new ArrayList<@NonNull Mapping>();
/**
* All context QVTi Mappings that predicate the QVTm Mappings.
*/
private final @NonNull List<@NonNull Mapping> parentMappings = new ArrayList<@NonNull Mapping>();
/**
* All refined QVTi Mappings (including this domain) that merge into the QVTm Mappings.
*/
private final @NonNull List<@NonNull Mapping> siblingMappings = new ArrayList<@NonNull Mapping>();
/**
* The merge for each domain.
*/
private final @NonNull Map<@Nullable TypedModel, @NonNull MergedDomain> uTypedModel2mergedDomain = new HashMap<@Nullable TypedModel, @NonNull MergedDomain>();
public MergedMapping(@NonNull CreateVisitor createVisitor, @NonNull Mapping uMapping) {
super(createVisitor);
this.uMapping = uMapping;
}
@Override
public boolean analyze() {
@SuppressWarnings("unused") String name = uMapping.getName();
computeChildMappings(childMappings, uMapping);
computeSiblingMappings(siblingMappings, uMapping);
for (@NonNull Mapping contextMapping : siblingMappings) {
if (contextMapping != uMapping) { // FIXME Legacy order
computeChildMappings(childMappings, contextMapping);
}
}
computeParentMappings(parentMappings, uMapping);
for (@NonNull Mapping uMapping : Iterables.concat(siblingMappings, parentMappings, childMappings)) {
for (@NonNull Domain uDomain : ClassUtil.nullFree(uMapping.getDomain())) {
TypedModel uTypedModel = QVTcoreUtil.getTypedModel(uDomain);
if (uTypedModel2mergedDomain.get(uTypedModel) == null) {
uTypedModel2mergedDomain.put(uTypedModel, new MergedDomain(createVisitor, this, uTypedModel, (@NonNull CoreDomain) uDomain));
}
}
}
for (@NonNull MergedDomain mergedDomain : uTypedModel2mergedDomain.values()) {
if (!mergedDomain.analyze()) {
return false;
}
}
return super.analyze();
}
private void computeChildMappings(@NonNull List<@NonNull Mapping> childMappings, @NonNull Mapping parentMapping) {
for (@NonNull Mapping childMapping : ClassUtil.nullFree(parentMapping.getLocal())) {
if (isFoldable(childMapping)) {
assert !childMappings.contains(childMapping) : "Local mappings cannot be cyclic";
childMappings.add(childMapping);
computeChildMappings(childMappings, childMapping);
}
}
}
private void computeParentMappings(@NonNull List<@NonNull Mapping> parentMappings, @NonNull Mapping childMapping) {
Mapping parentMapping = childMapping.getContext();
if (parentMapping != null) {
assert !parentMappings.contains(parentMapping) : "Context mappings cannot be cyclic";
computeSiblingMappings(parentMappings, parentMapping); // All siblings of the parent are also parents. ?? folded too ??
computeParentMappings(parentMappings, parentMapping);
}
}
private void computeSiblingMappings(@NonNull List<@NonNull Mapping> siblingMappings, @NonNull Mapping refinedMapping) {
if (!siblingMappings.contains(refinedMapping)) { // FIXME cannot be cyclic
siblingMappings.add(refinedMapping);
for (@NonNull Mapping specificationMapping : ClassUtil.nullFree(refinedMapping.getSpecification())) {
assert refinedMapping.getContext() == null : "Local mapping cannot refine another";
computeSiblingMappings(siblingMappings, specificationMapping);
}
}
}
@Override
protected @Nullable Mapping getArea(@NonNull Mapping uMapping) {
return uMapping;
}
@Override
protected @NonNull Iterable<@NonNull Mapping> getChildAreas() {
return childMappings;
}
@Override
protected @NonNull Mapping getMapping() {
return uMapping;
}
@Override
protected @NonNull MergedVariable getMergedVariable(@NonNull String name) {
for (@NonNull MergedDomain mergedDomain : uTypedModel2mergedDomain.values()) {
MergedVariable mergedVariable = mergedDomain.basicGetMergedVariable(name);
if (mergedVariable != null) {
return mergedVariable;
}
}
return super.getMergedVariable(name);
}
@Override
protected @NonNull Iterable<@NonNull Mapping> getParentAreas() {
return parentMappings;
}
@Override
protected @NonNull Iterable<@NonNull Mapping> getSiblingAreas() {
return siblingMappings;
}
public void synthesize(@NonNull Mapping mMapping) {
String name = createVisitor.getContext().getMappingName(uMapping);
mMapping.setName(name);
super.synthesize(mMapping, mMapping);
Transformation uTransformation = QVTbaseUtil.getContainingTransformation(uMapping);
for (@NonNull TypedModel uTypedModel : QVTbaseUtil.getModelParameters(uTransformation)) {
MergedDomain mergedDomain = uTypedModel2mergedDomain.get(uTypedModel);
if (mergedDomain != null) {
CoreDomain mDomain = mergedDomain.synthesize(mMapping);
mMapping.getDomain().add(mDomain);
}
}
}
@Override
public String toString() {
return uMapping.toString();
}
}
protected static class MergedVariable
{
public static final boolean GUARD = true;
public static final boolean BOTTOM = false;
protected final @NonNull CreateVisitor createVisitor;
protected final @NonNull MergedArea mergedArea;
protected final @NonNull String name;
private boolean isGuard = BOTTOM;
private boolean isRealized = false;
private @Nullable CompleteClass mergedType = null;
private boolean isRequired = false;
private @Nullable List<@NonNull Variable> variables = null;
private @Nullable List<@NonNull VariableAssignment> assignments = null;
protected MergedVariable(@NonNull MergedArea mergedArea, @NonNull String name) {
this.createVisitor = mergedArea.getCreateVisitor();
this.mergedArea = mergedArea;
this.name = name;
}
public void addAssignment(@NonNull VariableAssignment variableAssignment) {
List<@NonNull VariableAssignment> assignments2 = assignments;
if (assignments2 == null) {
assignments = assignments2 = new ArrayList<@NonNull VariableAssignment>();
}
assignments2.add(variableAssignment);
}
public void addVariable(@NonNull Variable variable, boolean isGuard) {
if (isGuard) {
this.isGuard = true;
}
else if (variable instanceof RealizedVariable) {
this.isRealized = true;
}
if (variable.isIsRequired()) {
this.isRequired = true;
}
List<@NonNull Variable> variables2 = variables;
if (variables2 == null) {
variables = variables2 = new ArrayList<@NonNull Variable>();
}
assert !variables2.contains(variable);
variables2.add(variable);
EnvironmentFactory environmentFactory = createVisitor.getContext().getEnvironmentFactory();
Type type = variable.getType();
assert type != null;
CompleteClass completeType = environmentFactory.getCompleteModel().getCompleteClass(type);
CompleteClass mergedType2 = mergedType;
if (mergedType2 == null) {
mergedType = mergedType2 = completeType;
}
else if (completeType.conformsTo(mergedType2)) {
mergedType = mergedType2 = completeType;
}
else {
assert mergedType2.conformsTo(completeType);
}
}
public void synthesize(@NonNull Mapping mMapping, @NonNull Area mArea) {
List<@NonNull VariableAssignment> assignments2 = assignments;
List<@NonNull Variable> variables2 = variables;
if (variables2 != null) {
Variable mVariable = QVTcoreFactory.eINSTANCE.createRealizedVariable();
if (isRealized) {
RealizedVariable realizedVariable = QVTcoreFactory.eINSTANCE.createRealizedVariable();
mVariable = realizedVariable;
mArea.getBottomPattern().getRealizedVariable().add(realizedVariable);
}
else {
if (isGuard) {
mVariable = QVTcoreFactory.eINSTANCE.createGuardVariable();
mArea.getGuardPattern().getVariable().add(mVariable);
}
else {
mVariable = QVTcoreFactory.eINSTANCE.createBottomVariable();
mArea.getBottomPattern().getVariable().add(mVariable);
}
}
mVariable.setName(name);
if (mergedType != null) {
mVariable.setType(mergedType.getPrimaryClass());
}
mVariable.setIsRequired(isRequired);
for (@NonNull Variable uVariable : variables2) {
createVisitor.getContext().addTrace(uVariable, mVariable);
createVisitor.createAll(uVariable.getOwnedComments(), mVariable.getOwnedComments());
}
if (assignments2 != null) {
// final Variable finalMVariable = mVariable;
// List<@NonNull VariableAssignment> mAssignments = new ArrayList<>();
createVisitor.createAll(assignments2, mMapping.getBottomPattern().getAssignment());
// mArea.getBottomPattern().getAssignment().addAll(mAssignments);
// for (@NonNull VariableAssignment uVariableAssignment : assignments2) {
// createVisitor.getContext().addTrace(uVariableAssignment, finalMVariable);
// }
/* createVisitor.getContext().addUpdater(finalMVariable, new Updater()
{
@Override
public void update(@NonNull UpdateVisitor updateVisitor) {
finalMVariable.setOwnedInit(updateVisitor.createCastCopy(assignments2.get(0).getValue(), finalMVariable.getType()));
for (@NonNull VariableAssignment uVariableAssignment : assignments2) {
createVisitor.getContext().addTrace(uVariableAssignment, finalMVariable);
createVisitor.createAll(uVariableAssignment.getOwnedComments(), finalMVariable.getOwnedComments());
}
}
}); */
}
}
else {
assert assignments2 != null;
VariableAssignment mVariableAssignment = createVisitor.create(assignments2.get(0));
assert mVariableAssignment != null;
// for (@NonNull VariableAssignment uVariableAssignment : assignments2) {
// createVisitor.getContext().addTrace(uVariableAssignment, mVariableAssignment);
// }
}
}
@Override
public String toString() {
return name;
}
}
protected static class UpdateVisitor extends AbstractUpdateVisitor<@NonNull QVTu2QVTm>
{
public UpdateVisitor(@NonNull QVTu2QVTm context) {
super(context);
}
@Override
public @Nullable Object visitVariable(@NonNull Variable mVariable) {
Updater updater = context.getUpdater(mVariable);
if (updater != null) {
updater.update(this);
return null;
}
else if (mVariable.eContainer() instanceof CorePattern) {
Variable uVariable = context.equivalentSource(mVariable);
mVariable.setOwnedInit(createCastCopy(uVariable.getOwnedInit(), uVariable.getType()));
return null;
}
else {
return super.visitVariable(mVariable);
}
}
}
protected static interface Updater
{
void update(@NonNull UpdateVisitor updateVisitor);
}
/**
* A local mapping may be folded into its context if it has no guard variables or predicates and so cannot execute independently of its context.
*/
private static boolean isFoldable(@NonNull Mapping mapping) {
if (mapping.getContext() == null) {
return false;
}
assert mapping.getSpecification().isEmpty() : " Local mappings cannot be a refinement.";
GuardPattern guardPattern = mapping.getGuardPattern();
if (guardPattern.getVariable().size() > 0) {
return false;
}
if (guardPattern.getPredicate().size() > 0) {
return false;
}
for (Domain d : mapping.getDomain()) {
CoreDomain cd = (CoreDomain)d;
guardPattern = cd.getGuardPattern();
if (guardPattern.getVariable().size() > 0) {
return false;
}
if (guardPattern.getPredicate().size() > 0) {
return false;
}
}
return true;
}
private final @NonNull Map<@NonNull Element, @NonNull Updater> element2updater = new HashMap<@NonNull Element, @NonNull Updater>();
private final @NonNull SymbolNameReservation symbolNameReservation = new SymbolNameReservation();
public QVTu2QVTm(@NonNull EnvironmentFactory environmentFactory) {
super(environmentFactory);
}
public void addUpdater(@NonNull Element mElement, @NonNull Updater updater) {
Updater oldUpdater = element2updater.put(mElement, updater);
assert oldUpdater == null;
}
@Override
protected @NonNull CreateVisitor createCreateVisitor() {
return new CreateVisitor(this);
}
@Override
protected @NonNull UpdateVisitor createUpdateVisitor() {
return new UpdateVisitor(this);
}
/**
* Return a unique name for uMapping. Local mappings have a name constructed from their context and guard variable names.
* All mappings have their names suffixed if necessary for uniqueness.
*/
public @NonNull String getMappingName(@NonNull Mapping uMapping) {
SymbolNameBuilder s = new SymbolNameBuilder();
String name = uMapping.getName();
if (name != null) {
s.appendName(name);
}
else {
Mapping uNamedMapping = uMapping;
while (uNamedMapping.getContext() != null) {
uNamedMapping = uNamedMapping.getContext();
}
s.appendString(PivotUtil.getName(uNamedMapping));
List<@NonNull String> guardVariableNames = new ArrayList<@NonNull String>();
for (@NonNull Variable guardVariable : ClassUtil.nullFree(uMapping.getGuardPattern().getVariable())) {
guardVariableNames.add(PivotUtil.getName(guardVariable));
}
for (@NonNull Domain uDomain : ClassUtil.nullFree(uMapping.getDomain())) {
for (@NonNull Variable guardVariable : ClassUtil.nullFree(((CoreDomain)uDomain).getGuardPattern().getVariable())) {
guardVariableNames.add(PivotUtil.getName(guardVariable));
}
}
Collections.sort(guardVariableNames);
for (@NonNull String guardVariableName : guardVariableNames) {
s.appendString("_");
s.appendString(guardVariableName);
}
}
return symbolNameReservation.reserveSymbolName(s, uMapping);
}
public @Nullable Updater getUpdater(@NonNull Element mElement) {
return element2updater.get(mElement);
}
private boolean hasVariables(Area a) {
return !(a.getGuardPattern().getVariable().isEmpty() ||
a.getBottomPattern().getVariable().isEmpty() ||
a.getBottomPattern().getRealizedVariable().isEmpty());
}
/**
* Return false if the mapping can be omitted, either because it is not refined or because it contributes no variables.
*/
private boolean isRequired(@NonNull Mapping m) {
List<@NonNull Mapping> refinements = ClassUtil.nullFree(m.getRefinement());
if (refinements.isEmpty())
return true;
for (@NonNull Mapping refining : refinements) {
for (Domain rd : refining.getDomain()) {
if (hasVariables((Area) rd))
return true;
}
if (hasVariables(m))
return true;
}
return false;
}
}