| /******************************************************************************* |
| * 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.common; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.common.util.WrappedException; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.Comment; |
| import org.eclipse.ocl.pivot.Element; |
| import org.eclipse.ocl.pivot.Import; |
| import org.eclipse.ocl.pivot.NamedElement; |
| import org.eclipse.ocl.pivot.NavigationCallExp; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.Operation; |
| import org.eclipse.ocl.pivot.OperationCallExp; |
| import org.eclipse.ocl.pivot.Package; |
| import org.eclipse.ocl.pivot.Parameter; |
| import org.eclipse.ocl.pivot.ParameterVariable; |
| import org.eclipse.ocl.pivot.PivotFactory; |
| import org.eclipse.ocl.pivot.PivotPackage; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.StandardLibrary; |
| 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.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.PivotConstants; |
| import org.eclipse.qvtd.pivot.qvtbase.Function; |
| import org.eclipse.qvtd.pivot.qvtbase.FunctionParameter; |
| 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.qvtbase.TypedModel; |
| import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil; |
| 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.GuardPattern; |
| import org.eclipse.qvtd.pivot.qvtcore.GuardVariable; |
| 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.QVTcorePackage; |
| import org.eclipse.qvtd.pivot.qvtcore.RealizedVariable; |
| import org.eclipse.qvtd.pivot.qvtcore.VariableAssignment; |
| import org.eclipse.qvtd.pivot.qvtcore.util.AbstractExtendingQVTcoreVisitor; |
| import org.eclipse.qvtd.pivot.qvtcore.utilities.QVTcoreHelper; |
| import org.eclipse.qvtd.pivot.qvtcore.utilities.QVTcoreUtil; |
| |
| /** |
| * AbstractQVTc2QVTc provides shared functionality for steps in the QVTC/QVTu/QVTm chain. |
| */ |
| public abstract class AbstractQVTc2QVTc |
| { |
| /** |
| * An ExpressionCopier deep copies an OCLExpression tree, exploiting the forward traceability of context to |
| * update references. Ambiguities should be resolved by performing the copy with respect to an appropriate |
| * stack of resolution scopes. |
| */ |
| @SuppressWarnings("serial") |
| protected static class ExpressionCopier extends EcoreUtil.Copier |
| { |
| private final @NonNull AbstractQVTc2QVTc context; |
| |
| public ExpressionCopier(@NonNull AbstractQVTc2QVTc context) { |
| this.context = context; |
| } |
| |
| @Override |
| public EObject get(Object oIn) { |
| EObject eOut = super.get(oIn); |
| if (eOut == null) { |
| eOut = context.equivalentTarget((Element)oIn); |
| } |
| return eOut; |
| } |
| } |
| |
| /** |
| * The CreateVisitor performs the mostly 1:1 creation of the QVTp output tree from the QVTm input tree. |
| * |
| * References are left unresolved. OCLExpressions are not copied. doXXX methods provide join points for derived implementations. |
| */ |
| protected static abstract class AbstractCreateVisitor<@NonNull C extends AbstractQVTc2QVTc> extends AbstractExtendingQVTcoreVisitor<Element, C> |
| { |
| public AbstractCreateVisitor(@NonNull C context) { |
| super(context); |
| } |
| |
| public <T extends Element> @Nullable T create(@Nullable T source) { |
| if (source == null) { |
| return null; |
| } |
| @SuppressWarnings("unchecked") @Nullable T target = (T) source.accept(this); |
| return target; |
| } |
| |
| public <T extends Element> void createAll(/*@NonNull*/ Iterable<T> sources, /*@NonNull*/ List<? super T> targets) { |
| for (@SuppressWarnings("null")@NonNull T source : sources) { |
| @SuppressWarnings("unchecked") T target = (T) source.accept(this); |
| if (target != null) { |
| targets.add(target); |
| } |
| } |
| } |
| |
| protected void doAssignments(@NonNull BottomPattern bIn, @NonNull BottomPattern bOut) { |
| createAll(bIn.getAssignment(), bOut.getAssignment()); |
| } |
| |
| protected void doLocalMappings(@NonNull Mapping mIn, @NonNull Mapping mOut) { |
| createAll(mIn.getLocal(), mOut.getLocal()); |
| } |
| |
| protected void doMapping(@NonNull Mapping mIn, @NonNull Mapping mOut) { |
| context.addTrace(mIn, mOut); |
| mOut.setName(mIn.getName()); |
| mOut.setGuardPattern(create(mIn.getGuardPattern())); |
| mOut.setBottomPattern(create(mIn.getBottomPattern())); |
| createAll(mIn.getDomain(), mOut.getDomain()); |
| doLocalMappings(mIn, mOut); |
| // createAll(mIn.getSpecification(), mOut.getSpecification()); |
| createAll(mIn.getOwnedComments(), mOut.getOwnedComments()); |
| } |
| |
| protected void doRealizedVariables(@NonNull BottomPattern bIn, @NonNull BottomPattern bOut) { |
| createAll(bIn.getRealizedVariable(), bOut.getRealizedVariable()); |
| } |
| |
| protected void doRules(@NonNull Transformation tIn, @NonNull Transformation tOut) { |
| createAll(tIn.getRule(), tOut.getRule()); |
| } |
| |
| // public @NonNull EnvironmentFactory getEnvironmentFactory() { |
| // return context.getEnvironmentFactory(); |
| // } |
| |
| @Override |
| public @Nullable Element visiting(@NonNull Visitable visitable) { |
| throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName()); |
| } |
| |
| @Override |
| public @NonNull BottomPattern visitBottomPattern(@NonNull BottomPattern bIn) { |
| BottomPattern bOut = QVTcoreFactory.eINSTANCE.createBottomPattern(); |
| context.addTrace(bIn, bOut); |
| doAssignments(bIn, bOut); |
| createAll(bIn.getPredicate(), bOut.getPredicate()); |
| doRealizedVariables(bIn, bOut); |
| createAll(bIn.getVariable(), bOut.getVariable()); |
| createAll(bIn.getOwnedComments(), bOut.getOwnedComments()); |
| return bOut; |
| } |
| |
| @Override |
| public @NonNull BottomVariable visitBottomVariable(@NonNull BottomVariable vIn) { |
| BottomVariable vOut = QVTcoreFactory.eINSTANCE.createBottomVariable(); |
| context.addTrace(vIn, vOut); |
| vOut.setName(vIn.getName()); |
| createAll(vIn.getOwnedComments(), vOut.getOwnedComments()); |
| return vOut; |
| } |
| |
| @Override |
| public @Nullable Element visitComment(@NonNull Comment cIn) { |
| Comment cOut = PivotFactory.eINSTANCE.createComment(); |
| context.addTrace(cIn, cOut); |
| cOut.setBody(cIn.getBody()); |
| createAll(cIn.getOwnedComments(), cOut.getOwnedComments()); |
| return cOut; |
| } |
| |
| @Override |
| public @NonNull CoreDomain visitCoreDomain(@NonNull CoreDomain dIn) { |
| CoreDomain dOut = QVTcoreFactory.eINSTANCE.createCoreDomain(); |
| context.addTrace(dIn, dOut); |
| dOut.setIsCheckable(dIn.isIsCheckable()); |
| dOut.setIsEnforceable(dIn.isIsEnforceable()); |
| dOut.setGuardPattern(create(dIn.getGuardPattern())); |
| dOut.setBottomPattern(create(dIn.getBottomPattern())); |
| createAll(dIn.getOwnedComments(), dOut.getOwnedComments()); |
| return dOut; |
| } |
| |
| @Override |
| public @NonNull CoreModel visitCoreModel(@NonNull CoreModel mIn) { |
| CoreModel mOut = QVTcoreFactory.eINSTANCE.createCoreModel(); |
| context.pushScope(mOut); |
| context.addTrace(mIn, mOut); |
| createAll(mIn.getOwnedImports(), mOut.getOwnedImports()); |
| createAll(mIn.getOwnedPackages(), mOut.getOwnedPackages()); |
| createAll(mIn.getOwnedComments(), mOut.getOwnedComments()); |
| context.popScope(); |
| return mOut; |
| } |
| |
| @Override |
| public @NonNull Function visitFunction(@NonNull Function fIn) { |
| Function fOut = QVTbaseFactory.eINSTANCE.createFunction(); |
| context.addTrace(fIn, fOut); // Global |
| context.pushScope(fOut); |
| fOut.setName(fIn.getName()); |
| fOut.setIsRequired(fIn.isIsRequired()); |
| fOut.setIsStatic(fIn.isIsStatic()); |
| fOut.setIsTransient(fIn.isIsTransient()); |
| fOut.setIsTypeof(fIn.isIsTypeof()); |
| createAll(fIn.getOwnedParameters(), fOut.getOwnedParameters()); |
| createAll(fIn.getOwnedComments(), fOut.getOwnedComments()); |
| context.popScope(); |
| return fOut; |
| } |
| |
| @Override |
| public @NonNull FunctionParameter visitFunctionParameter(@NonNull FunctionParameter fpIn) { |
| FunctionParameter fpOut = QVTbaseFactory.eINSTANCE.createFunctionParameter(); |
| context.addTrace(fpIn, fpOut); |
| fpOut.setName(fpIn.getName()); |
| fpOut.setIsRequired(fpIn.isIsRequired()); |
| fpOut.setIsTypeof(fpIn.isIsTypeof()); |
| createAll(fpIn.getOwnedComments(), fpOut.getOwnedComments()); |
| return fpOut; |
| } |
| |
| @Override |
| public @NonNull GuardPattern visitGuardPattern(@NonNull GuardPattern gIn) { |
| GuardPattern gOut = QVTcoreFactory.eINSTANCE.createGuardPattern(); |
| context.addTrace(gIn, gOut); |
| createAll(gIn.getPredicate(), gOut.getPredicate()); |
| createAll(gIn.getVariable(), gOut.getVariable()); |
| createAll(gIn.getOwnedComments(), gOut.getOwnedComments()); |
| return gOut; |
| } |
| |
| @Override |
| public @NonNull GuardVariable visitGuardVariable(@NonNull GuardVariable vIn) { |
| GuardVariable vOut = QVTcoreFactory.eINSTANCE.createGuardVariable(); |
| context.addTrace(vIn, vOut); |
| vOut.setName(vIn.getName()); |
| createAll(vIn.getOwnedComments(), vOut.getOwnedComments()); |
| return vOut; |
| } |
| |
| @Override |
| public @Nullable Element visitImport(@NonNull Import iIn) { |
| Import iOut = context.createImport(iIn); |
| createAll(iIn.getOwnedComments(), iOut.getOwnedComments()); |
| return iOut; |
| } |
| |
| @Override |
| public @Nullable Element visitMapping(@NonNull Mapping mIn) { |
| Mapping mOut = QVTcoreFactory.eINSTANCE.createMapping(); |
| context.pushScope(mOut); |
| doMapping(mIn, mOut); |
| context.popScope(); |
| return mOut; |
| } |
| |
| @Override |
| public final @Nullable Element visitNavigationAssignment(@NonNull NavigationAssignment object) { // Override must be of derived classes |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Element visitOppositePropertyAssignment(@NonNull OppositePropertyAssignment paIn) { |
| OppositePropertyAssignment paOut = QVTcoreFactory.eINSTANCE.createOppositePropertyAssignment(); |
| context.addTrace(paIn, paOut); |
| if (paIn.eIsSet(QVTcorePackage.Literals.ASSIGNMENT__IS_DEFAULT)) { |
| paOut.setIsDefault(paIn.isIsDefault()); |
| } |
| paOut.setTargetProperty(paIn.getTargetProperty()); |
| createAll(paIn.getOwnedComments(), paOut.getOwnedComments()); |
| return paOut; |
| } |
| |
| @Override |
| public @Nullable Element visitPackage(@NonNull Package pIn) { |
| if (PivotConstants.ORPHANAGE_URI.equals(pIn.getURI())) { |
| return null; |
| } |
| Package pOut = context.createPackage(pIn); |
| createAll(pIn.getOwnedClasses(), pOut.getOwnedClasses()); |
| createAll(pIn.getOwnedPackages(), pOut.getOwnedPackages()); |
| createAll(pIn.getOwnedComments(), pOut.getOwnedComments()); |
| return pOut; |
| } |
| |
| @Override |
| public @NonNull ParameterVariable visitParameterVariable(@NonNull ParameterVariable vIn) { |
| ParameterVariable vOut = PivotFactory.eINSTANCE.createParameterVariable(); |
| context.addTrace(vIn, vOut); |
| vOut.setName(vIn.getName()); |
| createAll(vIn.getOwnedComments(), vOut.getOwnedComments()); |
| return vOut; |
| } |
| |
| @Override |
| public @Nullable Element visitPredicate(@NonNull Predicate pIn) { |
| Predicate pOut = QVTbaseFactory.eINSTANCE.createPredicate(); |
| context.addTrace(pIn, pOut); |
| // The condition expression is copied during update once replacement variables exist. |
| createAll(pIn.getOwnedComments(), pOut.getOwnedComments()); |
| return pOut; |
| } |
| |
| @Override |
| public @Nullable Element visitPropertyAssignment(@NonNull PropertyAssignment paIn) { |
| PropertyAssignment paOut = QVTcoreFactory.eINSTANCE.createPropertyAssignment(); |
| context.addTrace(paIn, paOut); |
| if (paIn.eIsSet(QVTcorePackage.Literals.ASSIGNMENT__IS_DEFAULT)) { |
| paOut.setIsDefault(paIn.isIsDefault()); |
| } |
| paOut.setTargetProperty(paIn.getTargetProperty()); |
| createAll(paIn.getOwnedComments(), paOut.getOwnedComments()); |
| return paOut; |
| } |
| |
| @Override |
| public @NonNull Variable visitRealizedVariable(@NonNull RealizedVariable rvIn) { |
| Variable rvOut = QVTcoreFactory.eINSTANCE.createRealizedVariable(); |
| context.addTrace(rvIn, rvOut); |
| rvOut.setName(rvIn.getName()); |
| rvOut.setIsImplicit(rvIn.isIsImplicit()); |
| rvOut.setType(rvIn.getType()); |
| createAll(rvIn.getOwnedComments(), rvOut.getOwnedComments()); |
| return rvOut; |
| } |
| |
| @Override |
| public @NonNull Transformation visitTransformation(@NonNull Transformation tIn) { |
| Transformation tOut = QVTbaseFactory.eINSTANCE.createTransformation(); |
| context.addTrace(tIn, tOut); |
| tOut.setName(tIn.getName()); |
| tOut.setOwnedContext(create(tIn.getOwnedContext())); |
| createAll(tIn.getOwnedOperations(), tOut.getOwnedOperations()); |
| createAll(tIn.getModelParameter(), tOut.getModelParameter()); |
| doRules(tIn, tOut); |
| createAll(tIn.getOwnedComments(), tOut.getOwnedComments()); |
| return tOut; |
| } |
| |
| @Override |
| public @NonNull TypedModel visitTypedModel(@NonNull TypedModel tmIn) { |
| TypedModel tmOut = QVTbaseFactory.eINSTANCE.createTypedModel(); |
| context.addTrace(tmIn, tmOut); |
| String name = tmIn.getName(); |
| if (name == null) { |
| // name = QVTcoreUtil.MIDDLE_DOMAIN_NAME; |
| context.setMiddleTypedModelTarget(tmOut); |
| } |
| tmOut.setName(name); |
| tmOut.getUsedPackage().addAll(tmIn.getUsedPackage()); |
| createAll(tmIn.getOwnedComments(), tmOut.getOwnedComments()); |
| return tmOut; |
| } |
| |
| @Override |
| public @Nullable Element visitVariableAssignment(@NonNull VariableAssignment vaIn) { |
| VariableAssignment vaOut = QVTcoreFactory.eINSTANCE.createVariableAssignment(); |
| context.addTrace(vaIn, vaOut); |
| vaOut.setIsDefault(vaIn.isIsDefault()); |
| createAll(vaIn.getOwnedComments(), vaOut.getOwnedComments()); |
| return vaOut; |
| } |
| } |
| |
| /** |
| * The UpdateVisitor resolves the references and creates the OCLExpressions omitted by the CreateVisitor.. |
| */ |
| protected static abstract class AbstractUpdateVisitor<@NonNull C extends AbstractQVTc2QVTc> extends AbstractExtendingQVTcoreVisitor<Object, C> |
| { |
| private Operation equalsOperation = null; |
| protected final @NonNull QVTcoreHelper helper; |
| |
| public AbstractUpdateVisitor(@NonNull C context) { |
| super(context); |
| helper = context.getHelper(); |
| } |
| |
| /** |
| * Verify that variable references from the containment tree of outElement are to outElement's Resource. |
| * |
| * This may not be valid for multi-package transformations, but it's a very helpful debug for straightforward single package transformations. |
| */ |
| protected void checkOut(@NonNull Element pOut) { |
| Resource eResource = pOut.eResource(); |
| assert eResource != null; |
| for (TreeIterator<EObject> tit = pOut.eAllContents(); tit.hasNext(); ) { |
| EObject eObject = tit.next(); |
| if (eObject instanceof VariableExp) { |
| VariableDeclaration variable = ((VariableExp)eObject).getReferredVariable(); |
| Resource vResource = variable.eResource(); |
| // assert vResource == eResource; |
| if (vResource != eResource) { |
| System.err.println(variable + " : " + NameUtil.debugFullName(variable) + " not in output resource."); |
| vResource = variable.eResource(); |
| } |
| } |
| } |
| } |
| |
| protected Object convertToPredicate(@NonNull NavigationAssignment paIn, @NonNull Predicate pOut) { |
| OCLExpression slotExpression = copy(paIn.getSlotExpression()); |
| Property targetProperty = QVTcoreUtil.getTargetProperty(paIn); |
| assert (slotExpression != null) && (targetProperty != null); |
| OCLExpression valueExpression = copy(paIn.getValue()); |
| assert valueExpression != null; |
| NavigationCallExp propertyCallExp = helper.createNavigationCallExp(slotExpression, targetProperty); |
| context.addTrace(paIn, propertyCallExp); |
| propertyCallExp.eUnset(PivotPackage.Literals.TYPED_ELEMENT__IS_REQUIRED); // FIXME redundant compatibility |
| Operation equalsOperation = getEqualsOperation(); |
| OperationCallExp operationCallExp = helper.createOperationCallExp(propertyCallExp, equalsOperation, Collections.singletonList(valueExpression)); |
| context.addTrace(paIn, operationCallExp); |
| operationCallExp.setName(equalsOperation.getName()); // FIXME redundant compatibility |
| pOut.setConditionExpression(operationCallExp); |
| checkOut(pOut); |
| return null; |
| } |
| |
| protected @Nullable Object convertToVariableAssignment(@NonNull NavigationAssignment paIn, @NonNull VariableAssignment vaOut) { |
| OCLExpression veIn = paIn.getValue(); |
| assert veIn instanceof VariableExp; |
| VariableDeclaration vIn = ((VariableExp)veIn).getReferredVariable(); |
| assert vIn instanceof Variable; |
| Variable vOut = context.equivalentTarget((Variable)vIn); |
| vaOut.setTargetVariable(vOut); |
| OCLExpression slotExpression = copy(paIn.getSlotExpression()); |
| Property targetProperty = QVTcoreUtil.getTargetProperty(paIn); |
| assert (slotExpression != null) && (targetProperty != null); |
| NavigationCallExp propertyCallExp = helper.createNavigationCallExp(slotExpression, targetProperty); |
| context.addTrace(paIn, propertyCallExp); |
| propertyCallExp.eUnset(PivotPackage.Literals.TYPED_ELEMENT__IS_REQUIRED); // FIXME redundant compatibility |
| vaOut.setValue(propertyCallExp); |
| checkOut(vaOut); |
| return null; |
| } |
| |
| /** |
| * Copy the eIn tree replacing all references by their souce2targets mapping, using a non-null sibling to prefer a target |
| * in the same containing mapping. |
| */ |
| protected @Nullable <T extends Element> T copy(@Nullable T eIn) { |
| if (eIn == null) { |
| return null; |
| } |
| assert context != null; |
| EcoreUtil.Copier copier = new ExpressionCopier(context); |
| @SuppressWarnings("unchecked") T eOut = (T) copier.copy(eIn); |
| copier.copyReferences(); |
| context.addDebugCopies(copier); |
| return eOut; |
| } |
| |
| public @Nullable OCLExpression createCastCopy(@Nullable OCLExpression eIn, @Nullable Type toType) { |
| if ((eIn == null) || (toType == null)) { |
| return null; |
| } |
| OCLExpression eOut = copy(eIn); |
| if (eOut == null) { |
| return null; |
| } |
| Type eType = eOut.getType(); |
| StandardLibrary standardLibrary = context.getEnvironmentFactory().getStandardLibrary(); |
| if (eType.conformsTo(standardLibrary, toType)) { |
| return eOut; |
| } |
| assert (toType.conformsTo(standardLibrary, eType)); |
| return helper.createOperationCallExp(eOut, "oclAsType", helper.createTypeExp(toType)); |
| } |
| |
| protected @NonNull Mapping doMapping(@NonNull Mapping mOut) { |
| Mapping mIn = context.equivalentSource(mOut); |
| updateChild(mOut.getGuardPattern()); |
| updateChild(mOut.getBottomPattern()); |
| updateAllChildren(mOut.getDomain()); |
| updateAllChildren(mOut.getLocal()); |
| updateAllReferences(mIn.getSpecification(), mOut.getSpecification()); |
| return mIn; |
| } |
| |
| public @NonNull Operation getEqualsOperation() { |
| if (equalsOperation != null) { |
| return equalsOperation; |
| } |
| for (Operation op : context.environmentFactory.getStandardLibrary().getOclAnyType().getOwnedOperations()) { |
| if (op.getName().equals("=")) { |
| equalsOperation = op; |
| return op; |
| } |
| } |
| throw new IllegalStateException(); |
| } |
| |
| protected <T extends Element> void updateAllChildren(/*@NonNull*/ List<T> targets) { |
| for (@SuppressWarnings("null")@NonNull T target : targets) { |
| target.accept(this); |
| } |
| } |
| |
| protected <T extends Element> void updateAllReferences(/*@NonNull*/ List<T> sources, /*@NonNull*/ List<T> targets) { |
| for (@SuppressWarnings("null")@NonNull T source : sources) { |
| targets.add(context.equivalentTarget(source)); |
| } |
| } |
| |
| protected void updateChild(@Nullable Element target) { |
| if (target != null) { |
| target.accept(this); |
| } |
| } |
| |
| @Override |
| public @Nullable Object visiting(@NonNull Visitable visitable) { |
| throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName()); |
| } |
| |
| @Override |
| public @Nullable Object visitBottomPattern(@NonNull BottomPattern bOut) { |
| updateAllChildren(bOut.getAssignment()); |
| updateAllChildren(bOut.getPredicate()); |
| updateAllChildren(bOut.getRealizedVariable()); |
| updateAllChildren(bOut.getVariable()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitCoreDomain(@NonNull CoreDomain dOut) { |
| Element dIn = context.basicEquivalentSource(dOut); |
| TypedModel tmOut; |
| if (dIn instanceof CoreDomain) { |
| tmOut = context.equivalentTarget(QVTcoreUtil.getTypedModel((CoreDomain)dIn)); |
| } |
| else { |
| tmOut = context.getMiddleTypedModelTarget(); |
| } |
| dOut.setTypedModel(tmOut); |
| dOut.setName(tmOut.getName()); |
| updateChild(dOut.getGuardPattern()); |
| updateChild(dOut.getBottomPattern()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitCoreModel(@NonNull CoreModel mOut) { |
| @SuppressWarnings("unused") |
| CoreModel mIn = context.equivalentSource(mOut); |
| context.pushScope(mOut); |
| updateAllChildren(mOut.getOwnedPackages()); |
| context.popScope(); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Element visitFunction(@NonNull Function fOut) { |
| context.pushScope(fOut); |
| Function fIn = context.equivalentSource(fOut); |
| fOut.setQueryExpression(copy(fIn.getQueryExpression())); |
| fOut.setType(fIn.getType()); |
| // fOut.setTypeValue(fIn.getTypeValue()); |
| updateAllChildren(fOut.getOwnedParameters()); |
| context.popScope(); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitFunctionParameter(@NonNull FunctionParameter fpOut) { |
| FunctionParameter fpIn = context.equivalentSource(fpOut); |
| fpOut.setType(fpIn.getType()); |
| fpOut.setTypeValue(fpIn.getTypeValue()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitGuardPattern(@NonNull GuardPattern gOut) { |
| updateAllChildren(gOut.getPredicate()); |
| updateAllChildren(gOut.getVariable()); |
| for (Variable vOut : gOut.getVariable()) { |
| assert !(vOut instanceof RealizedVariable); |
| } |
| return null; |
| } |
| |
| @Override |
| public @NonNull Mapping visitMapping(@NonNull Mapping mOut) { |
| context.pushScope(mOut); |
| Mapping mIn = doMapping(mOut); |
| context.popScope(); |
| return mIn; |
| } |
| |
| @Override |
| public final Object visitNavigationAssignment(@NonNull NavigationAssignment object) { // Override must be of derived classes |
| return visiting(object); |
| } |
| |
| @Override |
| public @Nullable Element visitOppositePropertyAssignment(@NonNull OppositePropertyAssignment paOut) { |
| OppositePropertyAssignment paIn = context.equivalentSource(paOut); |
| paOut.setSlotExpression(copy(paIn.getSlotExpression())); |
| paOut.setValue(copy(paIn.getValue())); |
| checkOut(paOut); |
| return paIn; |
| } |
| |
| @Override |
| public @Nullable Object visitPackage(@NonNull Package pOut) { |
| updateAllChildren(pOut.getOwnedClasses()); |
| updateAllChildren(pOut.getOwnedPackages()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitParameter(@NonNull Parameter pOut) { |
| Parameter pIn = context.equivalentSource(pOut); |
| pOut.setName(pIn.getName()); |
| pOut.setIsRequired(pIn.isIsRequired()); |
| Type tVar = pIn.getType(); |
| pOut.setType(tVar); |
| pOut.setTypeValue(pIn.getTypeValue()); |
| return pIn; |
| } |
| |
| // |
| // Predicates that were PropertyAssignments need a comparison to be synthesized. |
| // |
| @Override |
| public @Nullable Object visitPredicate(@NonNull Predicate pOut) { |
| Element pIn = context.equivalentSource(pOut); |
| if (pIn instanceof NavigationAssignment) { |
| return convertToPredicate((NavigationAssignment)pIn, pOut); |
| } |
| else if (pIn instanceof Predicate) { |
| pOut.setConditionExpression(copy(((Predicate)pIn).getConditionExpression())); |
| checkOut(pOut); |
| return null; |
| } |
| else { |
| return visiting(pOut); |
| } |
| } |
| |
| @Override |
| public @Nullable Element visitPropertyAssignment(@NonNull PropertyAssignment paOut) { |
| PropertyAssignment paIn = context.equivalentSource(paOut); |
| paOut.setSlotExpression(copy(paIn.getSlotExpression())); |
| paOut.setValue(copy(paIn.getValue())); |
| checkOut(paOut); |
| return paIn; |
| } |
| |
| @Override |
| public @Nullable Object visitRealizedVariable(@NonNull RealizedVariable rvOut) { |
| RealizedVariable rvIn = context.equivalentSource(rvOut); |
| rvOut.setOwnedInit(copy(rvIn.getOwnedInit())); |
| return rvIn; |
| } |
| |
| @Override |
| public @Nullable Transformation visitTransformation(@NonNull Transformation tOut) { |
| Transformation tIn = context.equivalentSource(tOut); |
| updateChild(tOut.getOwnedContext()); |
| updateAllChildren(tOut.getOwnedOperations()); |
| updateAllChildren(tOut.getModelParameter()); |
| updateAllChildren(tOut.getRule()); |
| updateAllReferences(tIn.getSuperClasses(), tOut.getSuperClasses()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitTypedModel(@NonNull TypedModel tmOut) { |
| TypedModel tmIn = context.equivalentSource(tmOut); |
| updateAllReferences(tmIn.getDependsOn(), tmOut.getDependsOn()); |
| return null; |
| } |
| |
| @Override |
| public @Nullable Object visitVariable(@NonNull Variable vOut) { |
| Variable vIn = context.equivalentSource(vOut); |
| vOut.setName(vIn.getName()); |
| vOut.setIsImplicit(vIn.isIsImplicit()); |
| vOut.setIsRequired(vIn.isIsRequired()); |
| Type tIn = vIn.getType(); |
| Type tVar = tIn != null ? context.equivalentTarget(tIn) : null; |
| vOut.setType(tVar); |
| Type tvIn = vIn.getTypeValue(); |
| vOut.setTypeValue(tvIn != null ? context.equivalentTarget(tvIn) : null); |
| vOut.setOwnedInit(createCastCopy(vIn.getOwnedInit(), tVar)); |
| return vIn; |
| } |
| |
| @Override |
| public @Nullable Object visitVariableAssignment(@NonNull VariableAssignment vaOut) { |
| VariableAssignment vaIn = context.equivalentSource(vaOut); |
| vaOut.setTargetVariable(context.equivalentTarget(vaIn.getTargetVariable())); |
| vaOut.setValue(copy(vaIn.getValue())); |
| return vaIn; |
| } |
| } |
| |
| protected final @NonNull EnvironmentFactory environmentFactory; |
| protected final @NonNull QVTcoreHelper helper; |
| protected final @NonNull AbstractCreateVisitor<@NonNull ?> createVisitor; |
| protected final @NonNull AbstractUpdateVisitor<@NonNull ?> updateVisitor; |
| private TypedModel middleTypedModelTarget = null; |
| |
| /** |
| * Forward traceability from a source object to its targets for the current mapping. Top level entries ourside a maping are indexed for the null mapping. |
| */ |
| private final @NonNull Map<@Nullable NamedElement, @NonNull Map<@NonNull Element, @NonNull List<@NonNull Element>>> scope2source2targets = new HashMap<>(); |
| |
| private final @NonNull Stack<@NonNull NamedElement> scopeStack = new Stack<>(); |
| /** |
| * Reverse traceability from a target object to its source. |
| */ |
| private final @NonNull Map<@NonNull Element, @NonNull Element> target2source = new HashMap<>(); |
| |
| /** |
| * Reverse traceability from a target object to its source. |
| */ |
| private final @NonNull Map<@NonNull Element, @NonNull Element> debugCopy2source = new HashMap<>(); |
| |
| private @Nullable Resource debugSource = null; |
| private @Nullable Resource debugTarget = null; |
| |
| /** |
| * Create a new QVTc to QVTc transformation using an environmentFactory. |
| * |
| * It may be used once by invoking transform(). Repeated transform() calls are beyond the current design. |
| */ |
| protected AbstractQVTc2QVTc(@NonNull EnvironmentFactory environmentFactory) { |
| this.environmentFactory = environmentFactory; |
| this.helper = new QVTcoreHelper(environmentFactory); |
| this.createVisitor = createCreateVisitor(); |
| this.updateVisitor = createUpdateVisitor(); |
| } |
| |
| public void addDebugCopies(@NonNull Map<EObject, EObject> copier) { |
| for (EObject eSource : copier.keySet()) { |
| EObject eTarget = copier.get(eSource); |
| assert (eSource != null) && (eTarget != null); |
| debugCopy2source.put((Element)eTarget, (Element)eSource); |
| } |
| } |
| |
| /** |
| * Create a new trace for the given list of generated objects for the given |
| * context. |
| * |
| * @param source the source of the trace |
| * @param generated the list of generated objects |
| * @param context the context in which the trace is valid |
| */ |
| protected void addTrace(@NonNull Element source, @NonNull Element target) { |
| target2source.put(target, source); |
| // |
| NamedElement scope = scopeStack.peek(); |
| Map<@NonNull Element, @NonNull List<@NonNull Element>> source2targets = scope2source2targets.get(scope); |
| if (source2targets == null) { |
| source2targets = new HashMap<>(); |
| scope2source2targets.put(scope, source2targets); |
| } |
| List<@NonNull Element> targets = source2targets.get(source); |
| if (targets == null) { |
| targets = new ArrayList<>(); |
| source2targets.put(source, targets); |
| } |
| assert !targets.contains(target); |
| targets.add(target); |
| } |
| |
| protected @Nullable Element basicEquivalentSource(@Nullable Element target) { |
| if (target == null) { |
| return null; |
| } |
| assert target.eResource() != debugSource : "source element used for basicEquivalentSource " + target; |
| return target2source.get(target); |
| } |
| |
| protected abstract @NonNull AbstractCreateVisitor<@NonNull ?> createCreateVisitor(); |
| |
| protected @NonNull Import createImport(@NonNull Import iIn) { |
| Import iOut = helper.createImport(iIn.getName(), ClassUtil.nonNull(iIn.getImportedNamespace())); |
| addTrace(iIn, iOut); |
| return iOut; |
| } |
| |
| protected org.eclipse.ocl.pivot.@NonNull Package createPackage(org.eclipse.ocl.pivot.@NonNull Package pIn) { |
| org.eclipse.ocl.pivot.Package pOut = helper.createPackage(ClassUtil.nonNull(pIn.getName()), pIn.getNsPrefix(), pIn.getURI()); |
| addTrace(pIn, pOut); |
| return pOut; |
| } |
| |
| protected abstract @NonNull AbstractUpdateVisitor<@NonNull ?> createUpdateVisitor(); |
| |
| protected @NonNull <T extends Element> T equivalentSource(@NonNull T target) { |
| assert target.eResource() != debugSource : "source element used for equivalentSource " + target; |
| @SuppressWarnings("unchecked") T source = (T) target2source.get(target); |
| assert source != null; |
| return source; |
| } |
| |
| protected @NonNull <T extends Element> T equivalentTarget(/*@NonNull*/ T source) { |
| assert source != null; |
| assert source.eResource() != debugTarget : "target element used for equivalentTarget " + source; |
| List<@NonNull Element> targets = null; |
| for (int i = scopeStack.size(); (targets == null) && (--i >= 0); ) { |
| NamedElement scope = scopeStack.get(i); |
| Map<@NonNull Element, @NonNull List<@NonNull Element>> source2targets = scope2source2targets.get(scope); |
| if (source2targets != null) { |
| targets = source2targets.get(source); |
| } |
| } |
| if (targets != null) { |
| // assert targets.size() == 1; -- multiple entries are allowed, the first/primary entry is used |
| @SuppressWarnings("unchecked") T target = (T)targets.get(0); |
| assert target != null; |
| return target; |
| } |
| else { |
| return source; |
| } |
| } |
| |
| public @NonNull EnvironmentFactory getEnvironmentFactory() { |
| return environmentFactory; |
| } |
| |
| public @NonNull QVTcoreHelper getHelper() { |
| return helper; |
| } |
| |
| protected @NonNull TypedModel getMiddleTypedModelTarget() { |
| assert middleTypedModelTarget != null; |
| return middleTypedModelTarget; |
| } |
| |
| // protected @NonNull NamedElement getScope() { |
| // return scopeStack.peek(); |
| // } |
| |
| public void popScope() { |
| scopeStack.pop(); |
| } |
| |
| public void pushScope(@NonNull NamedElement scope) { |
| assert !scopeStack.contains(scope); |
| scopeStack.push(scope); |
| } |
| |
| protected void setDebugSource(@NonNull Resource debugSource) { |
| this.debugSource = debugSource; |
| } |
| |
| protected void setMiddleTypedModelTarget(@NonNull TypedModel middleTypedModelTarget) { |
| this.middleTypedModelTarget = middleTypedModelTarget; |
| } |
| |
| public void transform(@NonNull Resource source, @NonNull Resource target) throws IOException { |
| debugSource = source; |
| debugTarget = target; |
| // |
| // Do the transformation for each CoreModel. First a createVisitor descent of the input, then an updateVisitor descent of the output. |
| // |
| for (EObject eContent : source.getContents()) { |
| if (eContent instanceof CoreModel) { |
| CoreModel mIn = (CoreModel) eContent; |
| transform(mIn, target.getContents()); |
| } |
| } |
| // |
| // Debug code to conform that every output object is traceable to some input object. |
| // |
| for (TreeIterator<EObject> tit = target.getAllContents(); tit.hasNext(); ) { |
| EObject eTarget = tit.next(); |
| EObject eSource = target2source.get(eTarget); |
| EObject eCopied = debugCopy2source.get(eTarget); |
| if ((eSource == null) && (eCopied == null)) { |
| System.err.println("No source for " + eTarget.eClass().getName() + "@" + Integer.toString(System.identityHashCode(eTarget)) + ":" + eTarget + " / " + eTarget.eContainer().eClass().getName() + "@" + Integer.toString(System.identityHashCode(eTarget.eContainer()))); |
| } |
| } |
| |
| // FIXME the following lines should go obsolete |
| List<OperationCallExp> missingOperationCallSources = QVTbaseUtil.rewriteMissingOperationCallSources(environmentFactory, target); |
| if (missingOperationCallSources != null) { |
| System.err.println("Missing OperationCallExp sources were fixed up for '" + target.getURI() + "'"); |
| } |
| } |
| |
| protected void transform(@NonNull CoreModel mIn, @NonNull List<@NonNull EObject> mOuts) throws IOException { |
| try { |
| CoreModel mOut = (CoreModel) mIn.accept(createVisitor); |
| assert mOut != null; |
| mOuts.add(mOut); |
| mOut.accept(updateVisitor); |
| } |
| catch (WrappedException e) { |
| Throwable t = e.getCause(); |
| if (t instanceof IOException) { |
| throw (IOException)t; |
| } |
| throw e; |
| } |
| } |
| } |