| /******************************************************************************* |
| * 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.qvtc2qvtu; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.CallExp; |
| import org.eclipse.ocl.pivot.Element; |
| import org.eclipse.ocl.pivot.IfExp; |
| import org.eclipse.ocl.pivot.LiteralExp; |
| import org.eclipse.ocl.pivot.LoopExp; |
| import org.eclipse.ocl.pivot.NullLiteralExp; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.Operation; |
| import org.eclipse.ocl.pivot.OperationCallExp; |
| import org.eclipse.ocl.pivot.OppositePropertyCallExp; |
| import org.eclipse.ocl.pivot.PivotFactory; |
| import org.eclipse.ocl.pivot.PropertyCallExp; |
| 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.Transformation; |
| 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.BottomVariable; |
| 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.OppositePropertyAssignment; |
| import org.eclipse.qvtd.pivot.qvtcore.PropertyAssignment; |
| 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; |
| |
| /** |
| * QVTc2QVTu transforms a QVTc transformation to impose the single direction defined by a QVTuConfiguration. |
| * - the output domain is enforced |
| * - other domains are not-enforced |
| */ |
| public class QVTc2QVTu extends AbstractQVTc2QVTc |
| { |
| protected class CreateVisitor extends AbstractCreateVisitor<@NonNull QVTc2QVTu> |
| { |
| public CreateVisitor(@NonNull QVTc2QVTu context) { |
| super(context); |
| } |
| |
| private boolean allReferencedVariablesInInputDomain(@NonNull Element a) { |
| VariableDeclaration referredVariable = getReferredMappingVariable(a); |
| if ((referredVariable != null) && !isInputDomain(basicGetArea(referredVariable))) { |
| return false; |
| } |
| for (TreeIterator<EObject> tit = a.eAllContents(); tit.hasNext(); ) { |
| referredVariable = getReferredMappingVariable(tit.next()); |
| if ((referredVariable != null) && !isInputDomain(basicGetArea(referredVariable))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean allReferencedVariablesInOutputDomain(@NonNull Element a) { |
| VariableDeclaration referredVariable = getReferredMappingVariable(a); |
| if ((referredVariable != null) && !isOutputDomain(basicGetArea(referredVariable))) { |
| return false; |
| } |
| for (TreeIterator<EObject> tit = a.eAllContents(); tit.hasNext(); ) { |
| referredVariable = getReferredMappingVariable(tit.next()); |
| if ((referredVariable != null) && !isOutputDomain(basicGetArea(referredVariable))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean anyReferencedBottomMiddleDomainVariables(@NonNull OCLExpression value) { |
| VariableDeclaration referredVariable = getReferredMappingVariable(value); |
| if (isMiddleDomainVariable(referredVariable)) { |
| return true; |
| } |
| for (TreeIterator<EObject> tit = value.eAllContents(); tit.hasNext(); ) { |
| referredVariable = getReferredMappingVariable(tit.next()); |
| if (isMiddleDomainVariable(referredVariable)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean anyReferencedMiddleDomainVariables(@NonNull OCLExpression value) { |
| VariableDeclaration referredVariable = getReferredMappingVariable(value); |
| if ((referredVariable != null) && isMiddleDomain(basicGetArea(referredVariable))) { |
| return true; |
| } |
| for (TreeIterator<EObject> tit = value.eAllContents(); tit.hasNext(); ) { |
| referredVariable = getReferredMappingVariable(tit.next()); |
| if ((referredVariable != null) && isMiddleDomain(basicGetArea(referredVariable))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Create an ocl expression from an assignment than can be used as the |
| * condition expression of a predicate. |
| * |
| * @param aIn the a in |
| * @param environmentFactory the environment factory |
| * @return the operation call exp |
| */ |
| private OperationCallExp assignmentToOclExp(@NonNull Assignment aIn) { |
| OperationCallExp exp = PivotFactory.eINSTANCE.createOperationCallExp(); |
| for (Operation op : environmentFactory.getStandardLibrary().getOclAnyType().getOwnedOperations()) { |
| if (op.getName().equals("=")) { |
| exp.setReferredOperation(op); |
| exp.setName(op.getName()); |
| exp.setType(op.getType()); |
| break; |
| } |
| } |
| exp.getOwnedArguments().add(EcoreUtil.copy(aIn.getValue())); |
| if (aIn instanceof PropertyAssignment) { |
| PropertyCallExp sourceExp = PivotFactory.eINSTANCE.createPropertyCallExp(); |
| PropertyAssignment paIn = (PropertyAssignment) aIn; |
| sourceExp.setReferredProperty(paIn.getTargetProperty()); |
| sourceExp.setType(paIn.getTargetProperty().getType()); |
| sourceExp.setOwnedSource(EcoreUtil.copy(paIn.getSlotExpression())); |
| exp.setOwnedSource(sourceExp); |
| } else if (aIn instanceof OppositePropertyAssignment) { |
| OppositePropertyCallExp sourceExp = PivotFactory.eINSTANCE.createOppositePropertyCallExp(); |
| OppositePropertyAssignment opaIn = (OppositePropertyAssignment) aIn; |
| sourceExp.setReferredProperty(opaIn.getTargetProperty()); |
| sourceExp.setType(opaIn.getTargetProperty().getType()); |
| sourceExp.setOwnedSource(EcoreUtil.copy(opaIn.getSlotExpression())); |
| exp.setOwnedSource(sourceExp); |
| } else { // aIn instanceof VariableAssignment |
| VariableExp varExp = PivotFactory.eINSTANCE.createVariableExp(); |
| varExp.setIsImplicit(false); |
| varExp.setReferredVariable(((VariableAssignment) aIn).getTargetVariable()); |
| exp.setOwnedSource(varExp); |
| } |
| return exp; |
| } |
| |
| private @Nullable Area basicGetArea(@Nullable VariableDeclaration variable) { |
| return QVTcoreUtil.getContainingArea(variable); |
| } |
| |
| // |
| // Assignments may mutate to Predicates. |
| // |
| @Override |
| protected void doAssignments(@NonNull BottomPattern bIn, @NonNull BottomPattern bOut) { |
| GuardPattern gOut = context.equivalentTarget(bIn.getArea().getGuardPattern()); |
| assert gOut != null; |
| for (@NonNull Assignment aIn : ClassUtil.nullFree(bIn.getAssignment())) { |
| Element aOut = create(aIn); |
| if (aOut instanceof Predicate) { |
| bOut.getPredicate().add((Predicate) aOut); |
| } |
| else if (aOut instanceof Assignment) { |
| bOut.getAssignment().add((Assignment) aOut); |
| } |
| else if (aOut != null) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |
| |
| // |
| // Right-to-Middle and Local-to-Middle property assignments are discarded. |
| // Left-to-Left and Middle-to-Left property assignments change to predicates. |
| // Left defaults are non-defaults. |
| // |
| public @Nullable Element doNavigationAssignment(@NonNull NavigationAssignment paIn) { |
| OCLExpression slotExpression = paIn.getSlotExpression(); |
| OCLExpression value = paIn.getValue(); |
| assert (slotExpression != null) && (value != null); |
| // Area sourceArea = getSourceVariableArea(value); |
| Area targetArea = getSourceVariableArea(slotExpression); |
| // |
| // A backward "p.q := v" rewrites as a forward "v := p.q". |
| // |
| if (isInputDomain(targetArea) && (value instanceof VariableExp) && anyReferencedMiddleDomainVariables(value)) { // isMtoL |
| VariableAssignment vaOut = QVTcoreFactory.eINSTANCE.createVariableAssignment(); |
| context.addTrace(paIn, vaOut); |
| return vaOut; |
| } |
| if (isNonOutputDomain(targetArea) && // is RtoM or MtoL or RtoL |
| !(value instanceof VariableExp) && // why? |
| !(value instanceof NullLiteralExp) && // why? |
| allReferencedVariablesInOutputDomain(value)) { |
| return null; |
| } |
| if (isInputDomain(targetArea) && (targetArea instanceof Mapping) && anyReferencedBottomMiddleDomainVariables(value)) { // isMtoM |
| return null; |
| } |
| /* if (isInputDomain(sourceArea)) { |
| VariableDeclaration referredVariable = getReferredMappingVariable(value); |
| if (isMiddleDomainVariable(referredVariable)) { |
| // return null; |
| } |
| // if (referredVariable != null) { |
| // Area area = basicGetArea(referredVariable); |
| // if (isMiddleDomain(area) && (area instanceof BottomPattern)) { // FIXME are is never a BottomPattern |
| // return null; |
| // } |
| // } |
| } |
| if (isMiddleDomain(sourceArea)) { // isLocaltoM |
| VariableDeclaration referredVariable = getReferredMappingVariable(value); |
| if (isMiddleDomainVariable(referredVariable)) { |
| return null; |
| } |
| // if (referredVariable != null) { |
| // Area area = basicGetArea(referredVariable); |
| // if (isMiddleDomain(area) && (area instanceof BottomPattern)) { // FIXME are is never a BottomPattern |
| // return null; |
| // } |
| // } |
| } */ |
| |
| if (isInputDomain(targetArea) && anyReferencedMiddleDomainVariables(value)) { // isMtoL |
| // Assignments to Predicates |
| // Predicate pOut = QVTbaseFactory.eINSTANCE.createPredicate(); |
| // context.addTrace(paIn, pOut); |
| // pOut.setConditionExpression(MtcUtil.assignmentToOclExp(paIn, environmentFactory)); |
| // return pOut; |
| return null; |
| } |
| if (isInputDomain(targetArea) && allReferencedVariablesInInputDomain(paIn)) { |
| // Assignments to Predicates |
| Predicate pOut = QVTbaseFactory.eINSTANCE.createPredicate(); |
| context.addTrace(paIn, pOut); |
| pOut.setConditionExpression(assignmentToOclExp(paIn)); |
| return pOut; |
| } |
| NavigationAssignment paOut = paIn instanceof OppositePropertyAssignment |
| ? (NavigationAssignment) super.visitOppositePropertyAssignment((@NonNull OppositePropertyAssignment) paIn) |
| : (NavigationAssignment) super.visitPropertyAssignment((@NonNull PropertyAssignment) paIn); |
| assert paOut != null; |
| if (qvtuConfiguration.isCheckMode() && paIn.isIsDefault() && isOutputDomain(paIn.getBottomPattern().getArea())) { |
| // Default assignments |
| paOut.setIsDefault(false); |
| } |
| return paOut; |
| } |
| |
| // |
| // RealizedVariables may mutate to Variables. |
| // |
| @Override |
| protected void doRealizedVariables(@NonNull BottomPattern bIn, @NonNull BottomPattern bOut) { |
| GuardPattern gOut = context.equivalentTarget(bIn.getArea().getGuardPattern()); |
| assert gOut != null; |
| for (RealizedVariable rvIn : ClassUtil.nullFree(bIn.getRealizedVariable())) { |
| Variable vOut = create(rvIn); |
| if (vOut instanceof RealizedVariable) { |
| bOut.getRealizedVariable().add((RealizedVariable) vOut); |
| } |
| else { |
| gOut.getVariable().add(vOut); |
| } |
| } |
| } |
| |
| private @NonNull Area getContainingArea(@NonNull VariableDeclaration variable) { |
| Area targetArea = QVTcoreUtil.getContainingArea(variable); |
| assert targetArea != null; |
| return targetArea; |
| } |
| |
| private @Nullable VariableDeclaration getReferredMappingVariable(@Nullable EObject value) { |
| if (value instanceof VariableExp) { |
| VariableDeclaration referredVariable = ((VariableExp)value).getReferredVariable(); |
| EObject eContainer = referredVariable.eContainer(); |
| if (eContainer instanceof Transformation) { // "this" is not a mapping variable (has no domain) |
| return null; |
| } |
| if (eContainer instanceof LoopExp) { // iterators/accumulators are not mapping variables (has no domain) |
| return null; |
| } |
| return referredVariable; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| private @Nullable Area getSourceVariableArea(@Nullable OCLExpression exp) { |
| if (exp instanceof VariableExp) { |
| Variable expV = (Variable) ((VariableExp)exp).getReferredVariable(); |
| return basicGetArea(expV); |
| } else if (exp instanceof CallExp) { |
| return getSourceVariableArea(((CallExp) exp).getOwnedSource()); |
| } else if (exp instanceof IfExp) { |
| return getSourceVariableArea(((IfExp) exp).getOwnedCondition()); |
| } else if (exp instanceof LiteralExp) { |
| return null; |
| } |
| return null; |
| } |
| |
| private boolean isMiddleDomainVariable(@Nullable VariableDeclaration variable) { |
| if (variable == null) { |
| return false; |
| } |
| CorePattern pattern = QVTcoreUtil.getContainingPattern(variable); |
| if (!(pattern instanceof BottomPattern)) { |
| return false; |
| } |
| return pattern.getArea() instanceof Mapping; |
| } |
| |
| // |
| // CoreDomains enforcement is aligned to the direction |
| // |
| @Override |
| public @NonNull CoreDomain visitCoreDomain(@NonNull CoreDomain dIn) { |
| CoreDomain dOut = super.visitCoreDomain(dIn); |
| String name = QVTcoreUtil.getTypedModel(dIn).getName(); |
| dOut.setName(name); // Redundant replication of Epsilon functionality |
| if (qvtuConfiguration.isInput(name)) { |
| dOut.setIsEnforceable(false); |
| dOut.setIsCheckable(true); |
| } else { |
| if (qvtuConfiguration.isCheckMode()) { |
| dOut.setIsEnforceable(false); |
| } else if (qvtuConfiguration.isEnforceMode()) { |
| dOut.setIsCheckable(false); |
| } |
| } |
| return dOut; |
| } |
| |
| /* |
| * Override to redefine external URI. |
| */ |
| @Override |
| public @NonNull CoreModel visitCoreModel(@NonNull CoreModel mIn) { |
| CoreModel mOut = super.visitCoreModel(mIn); |
| String externalURI = mIn.getExternalURI(); |
| if (externalURI.endsWith(".qvtcas")) { |
| externalURI = externalURI.replace(".qvtcas", ".qvtu.qvtcas"); |
| } |
| else if (externalURI.endsWith(".qvtc")) { |
| externalURI = externalURI.replace(".qvtc", ".qvtu.qvtcas"); |
| } |
| mOut.setExternalURI(externalURI); |
| return mOut; |
| } |
| |
| /* |
| * Override to suppresses nested scopes since QVTc references can be cross-domain and we convert mappings 1:1. |
| */ |
| @Override |
| public @Nullable Element visitMapping(@NonNull Mapping mIn) { |
| MappingMode mappingMode = getMappingMode(mIn); |
| if (mappingMode == null) { |
| return null; |
| } |
| @NonNull Mapping mOut = QVTcoreFactory.eINSTANCE.createMapping(); |
| doMapping(mIn, mOut); |
| return mOut; |
| } |
| |
| @Override |
| public @Nullable Element visitOppositePropertyAssignment(@NonNull OppositePropertyAssignment paIn) { |
| return doNavigationAssignment(paIn); |
| } |
| |
| // |
| // Bottom pattern predicates invoving output variables are discarded; they are assignments. |
| // |
| @Override |
| public @Nullable Element visitPredicate(@NonNull Predicate pIn) { |
| // boolean oldResult = true; |
| // if ((pIn.getPattern() instanceof BottomPattern) && allReferencedVariablesInOutputDomain(pIn)) { |
| // oldResult = false; |
| // } |
| // else if ((pIn.getPattern() instanceof BottomPattern) && anyReferencedVariableInMiddleOrOutputDomain(pIn)) { |
| // oldResult = false; |
| // } |
| if ((pIn.getPattern() instanceof BottomPattern) && !allReferencedVariablesInInputDomain(pIn)) { |
| // assert !oldResult; |
| return null; |
| } |
| // if (!oldResult) { |
| // anyReferencedNonInputDomainVariables2(pIn); |
| // allReferencedVariablesInOutputDomain(pIn); |
| // anyReferencedVariableInMiddleOrOutputDomain(pIn); |
| // } |
| // assert oldResult; |
| /* OCLExpression conditionExpression = pIn.getConditionExpression(); |
| if (conditionExpression instanceof OperationCallExp) { |
| OperationCallExp callExp = (OperationCallExp)conditionExpression; |
| if ("=".equals(callExp.getReferredOperation().getName()) && (callExp.getOwnedArguments().size() == 1)) { // FIXME more accuracy |
| OCLExpression leftExpression= callExp.getOwnedSource(); |
| OCLExpression rightExpression= callExp.getOwnedArguments().get(0); |
| if ((leftExpression != null) && (rightExpression != null)) { |
| PropertyCallExp middlePropertyAccess = isMiddleDomainPropertyAccess(leftExpression); |
| if ((middlePropertyAccess != null) && isInputDomainExpression(rightExpression)) { |
| return convertToPropertyAssignment(middlePropertyAccess, rightExpression); |
| } |
| middlePropertyAccess = isMiddleDomainPropertyAccess(rightExpression); |
| if ((middlePropertyAccess != null) && isInputDomainExpression(leftExpression)) { |
| return convertToPropertyAssignment(middlePropertyAccess, leftExpression); |
| } |
| } |
| } |
| } */ |
| return super.visitPredicate(pIn); |
| } |
| |
| @Override |
| public @Nullable Element visitPropertyAssignment(@NonNull PropertyAssignment paIn) { |
| return doNavigationAssignment(paIn); |
| } |
| |
| // |
| // Input domain RealizedVariables are changed to unrealized Variables. |
| // |
| @Override |
| public @NonNull Variable visitRealizedVariable(@NonNull RealizedVariable rvIn) { |
| Area aIn = getContainingArea(rvIn); |
| if (isInputDomain(aIn)) { |
| BottomVariable vOut = QVTcoreFactory.eINSTANCE.createBottomVariable(); |
| assert vOut != null; |
| context.addTrace(rvIn, vOut); |
| vOut.setName(rvIn.getName()); |
| vOut.setType(rvIn.getType()); |
| vOut.setIsRequired(rvIn.isIsRequired()); |
| return vOut; |
| } |
| else { |
| return super.visitRealizedVariable(rvIn); |
| } |
| } |
| |
| // |
| // Right-to-Middle and Middle-to-Middle variable assignments are discarded. |
| // Middle-to-Left variable assignments change to predicates. |
| // |
| // FIXME why different to PropertyAssignments |
| // |
| @Override |
| public @Nullable Element visitVariableAssignment(@NonNull VariableAssignment vaIn) { |
| Variable targetVariable = vaIn.getTargetVariable(); |
| OCLExpression value = vaIn.getValue(); |
| assert (targetVariable != null) && (value != null); |
| Area targetArea = getContainingArea(targetVariable); |
| if (isInputDomain(targetArea) && !allReferencedVariablesInInputDomain(value)) { // an output-to-input assignment |
| return null; |
| } |
| // if (isInputDomain1(targetArea) && anyReferencedMiddleDomainVariables(value)) { // isMtoL |
| // return null; |
| // } |
| // if (isMiddleDomain(targetArea) /*&& !(value instanceof VariableExp)*/ && allMatchReferencedOutputDomainVariables(value)) { // isRtoM |
| // return null; |
| // } |
| // if ((targetArea instanceof Mapping) && anyReferencedBottomMiddleDomainVariables(value)) { // isMtoM |
| // return null; |
| // } |
| return super.visitVariableAssignment(vaIn); |
| } |
| } |
| |
| protected class UpdateVisitor extends AbstractUpdateVisitor<@NonNull QVTc2QVTu> |
| { |
| public UpdateVisitor(@NonNull QVTc2QVTu context) { |
| super(context); |
| } |
| |
| /* |
| * Override to suppresses nested scopes since QVTc references can be cross-domain and we convert mappings 1:1. |
| */ |
| @Override |
| public @NonNull Mapping visitMapping(@NonNull Mapping mOut) { |
| return doMapping(mOut); |
| } |
| |
| /* |
| * Override to handle the backward "p.q := v" rewrite as "v := p.q". |
| */ |
| @Override |
| public @Nullable Object visitVariableAssignment(@NonNull VariableAssignment vaOut) { |
| Element pIn = context.equivalentSource(vaOut); |
| if (pIn instanceof PropertyAssignment) { |
| return convertToVariableAssignment((PropertyAssignment)pIn, vaOut); |
| } |
| else { |
| return super.visitVariableAssignment(vaOut); |
| } |
| } |
| } |
| |
| private enum DomainMode { |
| INPUT, |
| MIDDLE, |
| OUTPUT |
| } |
| |
| private static final int IS_INPUT_MASK = 1; |
| private static final int IS_OUTPUT_MASK = 2; |
| |
| private enum MappingMode { |
| NULL(0), |
| LEFT2MIDDLE(IS_INPUT_MASK), |
| MIDDLE2RIGHT(IS_OUTPUT_MASK), |
| LEFT2RIGHT(IS_INPUT_MASK | IS_OUTPUT_MASK); |
| |
| private int flags; |
| |
| private MappingMode(int flags) { this.flags = flags; } |
| |
| private @NonNull MappingMode get(int flagsUnion) { |
| switch (flagsUnion) { |
| case 1 : return LEFT2MIDDLE; |
| case 2 : return MIDDLE2RIGHT; |
| case 3 : return LEFT2RIGHT; |
| } |
| return NULL; |
| } |
| |
| public @NonNull MappingMode asInput() { |
| return get(flags | IS_INPUT_MASK); |
| } |
| |
| public @NonNull MappingMode asOutput() { |
| return get(flags | IS_OUTPUT_MASK); |
| } |
| |
| public boolean hasInputDomain() { return (flags & IS_INPUT_MASK) != 0; } |
| public boolean hasOutputDomain() { return (flags & IS_OUTPUT_MASK) != 0; } |
| |
| public @NonNull MappingMode union(@NonNull MappingMode anotherMappingMode) { |
| return get(flags | anotherMappingMode.flags); |
| } |
| } |
| |
| private final @NonNull QVTuConfiguration qvtuConfiguration; |
| |
| /** |
| * Cached INPUT/MIDDLE/OUTPUT state of each Area, aggregating refined and/or local states. |
| */ |
| private final @NonNull Map<@NonNull Area, @NonNull DomainMode> domain2mode = new HashMap<@NonNull Area, @NonNull DomainMode>(); |
| |
| /** |
| * Cached LEFT2MIDDLE/MIDDLE2RIGHT/LEFT2RIGHT state of each top or local Mapping aggregating refined states. |
| */ |
| private final @NonNull Map<@NonNull Mapping, @NonNull MappingMode> mapping2mode = new HashMap<@NonNull Mapping, @NonNull MappingMode>(); |
| |
| public QVTc2QVTu(@NonNull EnvironmentFactory environmentFactory, @NonNull QVTuConfiguration qvtuConfiguration) { |
| super(environmentFactory); |
| this.qvtuConfiguration = qvtuConfiguration; |
| } |
| |
| @Override |
| protected @NonNull CreateVisitor createCreateVisitor() { |
| return new CreateVisitor(this); |
| } |
| |
| @Override |
| protected @NonNull UpdateVisitor createUpdateVisitor() { |
| return new UpdateVisitor(this); |
| } |
| |
| private @Nullable MappingMode getComposedMappingMode(@NonNull Mapping mapping) { |
| MappingMode mergedMode = MappingMode.NULL; |
| for (@NonNull Domain domain: ClassUtil.nullFree(mapping.getDomain())) { |
| String name = PivotUtil.getName(QVTcoreUtil.getTypedModel(domain)); |
| if (qvtuConfiguration.isInput(name)) { |
| mergedMode = mergedMode.asInput(); |
| domain2mode.put((CoreDomain)domain, DomainMode.INPUT); |
| } |
| else if (qvtuConfiguration.isOutput(name)) { |
| if (!domain.isIsEnforceable()) { |
| return null; |
| } |
| mergedMode = mergedMode.asOutput(); |
| domain2mode.put((CoreDomain)domain, DomainMode.OUTPUT); |
| } |
| } |
| for (@NonNull Mapping localMapping : ClassUtil.nullFree(mapping.getLocal())) { |
| MappingMode mappingMode = getMappingMode(localMapping); |
| if (mappingMode == null) { |
| return null; |
| } |
| mergedMode = mergedMode.union(mappingMode); |
| } |
| return mergedMode; |
| } |
| |
| private @Nullable MappingMode getMappingMode(@NonNull Mapping mapping) { |
| MappingMode mergedMode = mapping2mode.get(mapping); |
| if (mergedMode == null) { |
| mergedMode = getComposedMappingMode(mapping); |
| if (mergedMode == null) { |
| return null; |
| } |
| for (@NonNull Mapping refinedMapping : ClassUtil.nullFree(mapping.getSpecification())) { |
| MappingMode composedMappingMode = getComposedMappingMode(refinedMapping); |
| if (composedMappingMode == null) { |
| return null; |
| } |
| mergedMode = mergedMode.union(composedMappingMode); |
| } |
| mapping2mode.put(mapping, mergedMode); |
| setMappingDomainModes(mapping, mergedMode); |
| } |
| return mergedMode; |
| } |
| |
| private boolean isInputDomain(@Nullable Area area) { |
| if (area == null) { |
| return false; |
| } |
| DomainMode domainMode = domain2mode.get(area); |
| assert domainMode != null; |
| return domainMode == DomainMode.INPUT; |
| } |
| |
| private boolean isMiddleDomain(@Nullable Area area) { |
| if (area == null) { |
| return false; |
| } |
| DomainMode domainMode = domain2mode.get(area); |
| assert domainMode != null; |
| return domainMode == DomainMode.MIDDLE; |
| } |
| |
| private boolean isNonOutputDomain(@Nullable Area area) { |
| if (area == null) { |
| return false; |
| } |
| DomainMode domainMode = domain2mode.get(area); |
| assert domainMode != null; |
| return domainMode != DomainMode.OUTPUT; |
| } |
| |
| private boolean isOutputDomain(@Nullable Area area) { |
| if (area == null) { |
| return false; |
| } |
| DomainMode domainMode = domain2mode.get(area); |
| assert domainMode != null; |
| return domainMode == DomainMode.OUTPUT; |
| } |
| |
| private void setMappingDomainModes(@NonNull Mapping mapping, @NonNull MappingMode mode) { |
| domain2mode.put(mapping, !mode.hasInputDomain() ? DomainMode.INPUT : !mode.hasOutputDomain() ? DomainMode.OUTPUT : DomainMode.MIDDLE); |
| for (@NonNull Mapping localMapping : ClassUtil.nullFree(mapping.getLocal())) { |
| setMappingDomainModes(localMapping, mode); |
| } |
| } |
| } |