| /******************************************************************************* |
| * Copyright (c) 2016, 2017 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * E.D.Willink - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.qvtd.umlx.umlx2qvtr; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.Element; |
| import org.eclipse.ocl.pivot.Import; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager; |
| import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal; |
| import org.eclipse.ocl.pivot.utilities.EnvironmentFactory; |
| import org.eclipse.ocl.pivot.utilities.MetamodelManager; |
| import org.eclipse.ocl.pivot.utilities.NameUtil; |
| import org.eclipse.ocl.pivot.utilities.ParserException; |
| import org.eclipse.ocl.pivot.utilities.PivotUtil; |
| import org.eclipse.qvtd.compiler.CompilerChainException; |
| import org.eclipse.qvtd.pivot.qvtbase.Function; |
| import org.eclipse.qvtd.pivot.qvtbase.FunctionParameter; |
| import org.eclipse.qvtd.pivot.qvtbase.Rule; |
| import org.eclipse.qvtd.pivot.qvtbase.TypedModel; |
| import org.eclipse.qvtd.pivot.qvtbase.utilities.QVTbaseUtil; |
| import org.eclipse.qvtd.pivot.qvtrelation.Key; |
| import org.eclipse.qvtd.pivot.qvtrelation.QVTrelationFactory; |
| import org.eclipse.qvtd.pivot.qvtrelation.RelationModel; |
| import org.eclipse.qvtd.pivot.qvtrelation.RelationalTransformation; |
| import org.eclipse.qvtd.pivot.qvtrelation.utilities.QVTrelationHelper; |
| import org.eclipse.qvtd.pivot.qvtrelation.utilities.QVTrelationUtil; |
| import org.eclipse.qvtd.umlx.RelDiagram; |
| import org.eclipse.qvtd.umlx.TxDiagram; |
| import org.eclipse.qvtd.umlx.TxKeyNode; |
| import org.eclipse.qvtd.umlx.TxPackageNode; |
| import org.eclipse.qvtd.umlx.TxParameterNode; |
| import org.eclipse.qvtd.umlx.TxPartNode; |
| import org.eclipse.qvtd.umlx.TxQueryNode; |
| import org.eclipse.qvtd.umlx.TxTypedModelNode; |
| import org.eclipse.qvtd.umlx.UMLXElement; |
| import org.eclipse.qvtd.umlx.UMLXModel; |
| import org.eclipse.qvtd.umlx.UMLXTypedElement; |
| import org.eclipse.qvtd.umlx.util.AbstractExtendingUMLXVisitor; |
| import org.eclipse.qvtd.umlx.utilities.UMLXUtil; |
| |
| import com.google.common.collect.Iterables; |
| |
| public class UMLX2QVTr extends QVTrelationHelper |
| { |
| protected static class CreateVisitor extends AbstractExtendingUMLXVisitor<@Nullable Element, @NonNull UMLX2QVTr> |
| { |
| protected final @NonNull MetamodelManager metamodelManager; |
| protected final @NonNull RelationModel qvtrModel; |
| |
| public CreateVisitor(@NonNull UMLX2QVTr context, @NonNull RelationModel qvtrModel) { |
| super(context); |
| EnvironmentFactory environmentFactory = context.getEnvironmentFactory(); |
| this.metamodelManager = environmentFactory.getMetamodelManager(); |
| this.qvtrModel = qvtrModel; |
| } |
| |
| private <@NonNull T1 extends UMLXElement, T2 extends Element> void createAll(/*@NonNull*/ Iterable<T1> sources, /*@NonNull*/ List<? super T2> targets) { |
| for (T1 source : sources) { |
| @SuppressWarnings("unchecked") T2 target = (T2) source.accept(this); |
| if ((target != null) && (targets != null)) { |
| targets.add(target); |
| } |
| } |
| } |
| |
| private org.eclipse.ocl.pivot.@NonNull Package getPackage(org.eclipse.ocl.pivot.@Nullable Package asParentPackage, @NonNull String name) { |
| List<org.eclipse.ocl.pivot.@NonNull Package> asPackages = (asParentPackage != null ? QVTbaseUtil.Internal.getOwnedPackagesList(asParentPackage) : QVTbaseUtil.Internal.getOwnedPackagesList(qvtrModel)); |
| org.eclipse.ocl.pivot.Package qvtrPackage = NameUtil.getNameable(asPackages, name); |
| if (qvtrPackage == null) { |
| @NonNull String nsURI = "http:/fixme"; // FIXME |
| qvtrPackage = PivotUtil.createPackage(name, null, nsURI, null); |
| qvtrPackage.setURI(null); |
| asPackages.add(qvtrPackage); |
| } |
| return qvtrPackage; |
| } |
| |
| private void visitAll(/*@NonNull*/ Iterable<? extends @NonNull UMLXElement> sources) { |
| for (@NonNull UMLXElement source : sources) { |
| source.accept(this); |
| } |
| } |
| |
| @Override |
| public @Nullable Element visitRelDiagram(@NonNull RelDiagram relDiagram) { |
| PatternForest patternForest = new PatternForest(context, relDiagram); |
| context.patternForests.add(patternForest); |
| return patternForest.create(); // Pass 1: create the elements |
| } |
| |
| @Override |
| public @Nullable Element visitTxDiagram(@NonNull TxDiagram txDiagram) { |
| List<@NonNull Key> qvtrKeys = new ArrayList<>(); |
| createAll(UMLXUtil.getOwnedTxKeyNodes(txDiagram), qvtrKeys); |
| List<@NonNull TypedModel> qvtrTypedModels = new ArrayList<>(); |
| createAll(UMLXUtil.getOwnedTxTypedModelNodes(txDiagram), qvtrTypedModels); |
| RelationalTransformation qvtrRelationalTransformation = context.createRelationalTransformation(UMLXUtil.getName(txDiagram), qvtrTypedModels); |
| context.install(txDiagram, qvtrRelationalTransformation); |
| org.eclipse.ocl.pivot.Package asPackage = null; |
| String packagePath = txDiagram.getPackage(); |
| if (packagePath != null) { |
| for (StringTokenizer tokenizer = new StringTokenizer(packagePath, "::"); tokenizer.hasMoreTokens(); ) { |
| String token = tokenizer.nextToken(); |
| asPackage = getPackage(asPackage, token); |
| } |
| } |
| if (asPackage == null) { |
| asPackage = getPackage(null, ""); |
| } |
| // Collections.sort(qvtrKeys, NameUtil.NAMEABLE_COMPARATOR); |
| Iterables.addAll(QVTrelationUtil.Internal.getOwnedKeysList(qvtrRelationalTransformation), qvtrKeys); |
| QVTbaseUtil.Internal.getOwnedClassesList(asPackage).add(qvtrRelationalTransformation); |
| // |
| List<@NonNull Rule> allRelationsList = new ArrayList<>(); |
| createAll(UMLXUtil.getOwnedRelDiagrams(txDiagram), allRelationsList); |
| createAll(UMLXUtil.getOwnedTxQueryNodes(txDiagram), QVTbaseUtil.Internal.getOwnedOperationsList(qvtrRelationalTransformation)); |
| // |
| for (@NonNull TxTypedModelNode txTypedModelNode : UMLXUtil.getOwnedTxTypedModelNodes(txDiagram)) { |
| TypedModel asTypedModel = context.getQVTrElement(TypedModel.class, txTypedModelNode); |
| for (@NonNull TxTypedModelNode txDependsOn : UMLXUtil.getDependsOns(txTypedModelNode)) { |
| TypedModel asDependsOn = context.getQVTrElement(TypedModel.class, txDependsOn); |
| asTypedModel.getDependsOn().add(asDependsOn); |
| } |
| } |
| // Collections.sort(allRelationsList, NameUtil.NAMEABLE_COMPARATOR); |
| List<@NonNull Import> qvtrImports = QVTrelationUtil.Internal.getOwnedImportsList(qvtrModel); |
| for (@NonNull TxPackageNode txPackageNode : UMLXUtil.getOwnedTxPackageNodes(txDiagram)) { |
| for (@NonNull String name : UMLXUtil.getImportAliases(txPackageNode)) { |
| try { |
| org.eclipse.ocl.pivot.Package asImportedPackage = metamodelManager.getASOf(org.eclipse.ocl.pivot.Package.class, txPackageNode.getReferredEPackage()); |
| if (asImportedPackage != null) { |
| Import qvtrImport = NameUtil.getNameable(qvtrImports, name); |
| if (qvtrImport != null) { |
| assert qvtrImport.getImportedNamespace() == asImportedPackage; |
| } |
| else { |
| qvtrImport = context.createImport(name, asImportedPackage); |
| qvtrImports.add(qvtrImport); |
| } |
| } |
| } catch (ParserException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| // |
| Iterables.addAll(QVTrelationUtil.Internal.getOwnedRelationsList(qvtrRelationalTransformation), allRelationsList); |
| QVTbaseUtil.getContextVariable(metamodelManager.getStandardLibrary(), qvtrRelationalTransformation); |
| return qvtrRelationalTransformation; |
| } |
| |
| @Override |
| public @Nullable Element visitTxKeyNode(@NonNull TxKeyNode txKeyNode) { |
| EClass eClass = UMLXUtil.getReferredEClass(txKeyNode); |
| org.eclipse.ocl.pivot.Class asClass = metamodelManager.getASOfEcore(org.eclipse.ocl.pivot.Class.class, eClass); |
| assert asClass != null; |
| List<@NonNull Property> asProperties = new ArrayList<>(); |
| createAll(UMLXUtil.getOwnedTxPartNodes(txKeyNode), asProperties); |
| Key asKey = context.createKey(asClass, asProperties); |
| context.install(txKeyNode, asKey); |
| return asKey; |
| } |
| |
| // @Override |
| // public @Nullable Element visitTxPackageNode(@NonNull TxPackageNode txPackageNode) { |
| // EPackage ePackage = UMLXUtil.getReferredEPackage(txPackageNode); |
| // return metamodelManager.getASOfEcore(org.eclipse.ocl.pivot.Package.class, ePackage); |
| // } |
| |
| @Override |
| public @Nullable Element visitTxPartNode(@NonNull TxPartNode txPartNode) { |
| EStructuralFeature eStructuralFeature = UMLXUtil.getReferredEStructuralFeature(txPartNode); |
| Property asProperty = metamodelManager.getASOfEcore(Property.class, eStructuralFeature); |
| assert asProperty != null; |
| if (txPartNode.isIsOpposite()) { |
| asProperty = asProperty.getOpposite(); |
| } |
| return asProperty; |
| } |
| |
| @Override |
| public @Nullable Element visitTxParameterNode(@NonNull TxParameterNode txParameterNode) { |
| org.eclipse.ocl.pivot.@NonNull Class asClass = context.getReferredType(txParameterNode); |
| FunctionParameter asFunctionParameter = context.createFunctionParameter(UMLXUtil.getName(txParameterNode), asClass, txParameterNode.isIsRequired()); |
| context.install(txParameterNode, asFunctionParameter); |
| return asFunctionParameter; |
| } |
| |
| @Override |
| public @Nullable Element visitTxQueryNode(@NonNull TxQueryNode txQueryNode) { |
| List<@NonNull FunctionParameter> qvtrParameters = new ArrayList<>(); |
| createAll(UMLXUtil.getOwnedTxParameterNodes(txQueryNode), qvtrParameters); |
| org.eclipse.ocl.pivot.@NonNull Class asClass = context.getReferredType(txQueryNode); |
| Function asFunction = context.createFunction(UMLXUtil.getName(txQueryNode), asClass, txQueryNode.isIsRequired(), qvtrParameters); |
| context.install(txQueryNode, asFunction); |
| context.txQueryNodes.add(txQueryNode); |
| return asFunction; |
| } |
| |
| @Override |
| public @Nullable Element visitTxTypedModelNode(@NonNull TxTypedModelNode txTypedModelNode) { |
| List<org.eclipse.ocl.pivot.@NonNull Package> usedPackages = new ArrayList<>(); |
| for (@NonNull TxPackageNode txPackageNode : UMLXUtil.getUsedTxPackageNodes(txTypedModelNode)) { |
| try { |
| org.eclipse.ocl.pivot.Package asPackage = metamodelManager.getASOf(org.eclipse.ocl.pivot.Package.class, txPackageNode.getReferredEPackage()); |
| if (asPackage != null) { |
| usedPackages.add(asPackage); |
| } |
| } catch (ParserException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| TypedModel asTypedModel = context.createTypedModel(UMLXUtil.getName(txTypedModelNode), usedPackages); |
| context.install(txTypedModelNode, asTypedModel); |
| return asTypedModel; |
| } |
| |
| @Override |
| public @Nullable Element visitUMLXModel(@NonNull UMLXModel umlxModel) { |
| context.install(umlxModel, qvtrModel); |
| Iterable<@NonNull TxDiagram> txDiagrams = UMLXUtil.getOwnedTxDiagrams(umlxModel); |
| visitAll(txDiagrams); |
| /* for (@NonNull TxDiagram txDiagram : txDiagrams) { |
| for (@NonNull TxTypedModelNode txTypedModelNode : UMLXUtil.getOwnedTxTypedModelNodes(txDiagram)) { |
| String name = txTypedModelNode.getName(); |
| if (name != null) { |
| String uriString = txTypedModelNode.getUri(); |
| if (uriString != null) { |
| URI uri = URI.createURI(uriString); |
| Resource umlxResource = umlxModel.eResource(); |
| if (umlxResource != null) { |
| URI resourceURI = umlxResource.getURI(); |
| uri = uri.resolve(resourceURI); |
| } |
| EObject eObject = context.getEnvironmentFactory().getResourceSet().getEObject(uri, true); |
| try { |
| Namespace asNamespace = metamodelManager.getASOf(Namespace.class, eObject); |
| if (asNamespace != null) { |
| |
| |
| Import asImport = context.createImport(name, asNamespace); |
| // context.install(txImportNode, asImport); |
| qvtrModel.getOwnedImports().add(asImport); |
| return asImport; |
| } |
| } catch (ParserException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } */ |
| return null; |
| } |
| |
| @Override |
| public @Nullable Element visiting(@NonNull UMLXElement umlxElement) { |
| System.out.println("Unsupported " + umlxElement.eClass().getName() + " for " + getClass().getSimpleName()); |
| return null; |
| // throw new IllegalArgumentException("Unsupported " + visitable.eClass().getName() + " for " + getClass().getSimpleName()); |
| } |
| } |
| |
| private final @NonNull Resource umlxResource; |
| private final @NonNull Resource qvtrResource; |
| private final @NonNull Map<@NonNull UMLXElement, @NonNull Element> umlxElement2qvtrElement = new HashMap<>(); |
| private final @NonNull List<@NonNull PatternForest> patternForests = new ArrayList<>(); |
| private final @NonNull List<@NonNull TxQueryNode> txQueryNodes = new ArrayList<>(); |
| |
| public UMLX2QVTr(@NonNull EnvironmentFactory environmentFactory, @NonNull Resource umlxResource, @NonNull Resource qvtrResource) { |
| super(environmentFactory); |
| this.umlxResource = umlxResource; |
| this.qvtrResource = qvtrResource; |
| } |
| |
| protected <T extends Element> @Nullable T basicGetQVTrElement(@NonNull Class<T> qvtrClass, @NonNull UMLXElement umlxElement) { |
| Element qvtrElement = umlxElement2qvtrElement.get(umlxElement); |
| if (qvtrElement == null) { |
| return null; |
| } |
| if (!qvtrClass.isAssignableFrom(qvtrElement.getClass())) { |
| throw new ClassCastException("QVTr element " + umlxElement + " cannot be cast to " + qvtrClass); |
| } |
| @SuppressWarnings("unchecked")T castElement = (T)qvtrElement; |
| return castElement; |
| } |
| |
| @Override |
| public @NonNull EnvironmentFactory getEnvironmentFactory() { |
| return environmentFactory; |
| } |
| |
| protected <T extends Element> @NonNull T getQVTrElement(@NonNull Class<T> qvtrClass, @NonNull UMLXElement umlxElement) { |
| Element qvtrElement = umlxElement2qvtrElement.get(umlxElement); |
| if (qvtrElement == null) { |
| throw new IllegalArgumentException("Missing UMLX element for " + umlxElement); |
| } |
| if (!qvtrClass.isAssignableFrom(qvtrElement.getClass())) { |
| throw new ClassCastException("QVTr element " + umlxElement + " cannot be cast to " + qvtrClass); |
| } |
| @SuppressWarnings("unchecked")T castElement = (T)qvtrElement; |
| return castElement; |
| } |
| |
| protected org.eclipse.ocl.pivot.@NonNull Class getReferredType(@NonNull UMLXTypedElement umlxTypedElement) { |
| PivotMetamodelManager metamodelManager = (PivotMetamodelManager) environmentFactory.getMetamodelManager(); |
| EClassifier eClassifier = UMLXUtil.getReferredEClassifier(umlxTypedElement); |
| org.eclipse.ocl.pivot.Class asClass = metamodelManager.getASOfEcore(org.eclipse.ocl.pivot.Class.class, eClassifier); |
| assert asClass != null; |
| if (umlxTypedElement.isIsMany()) { |
| asClass = metamodelManager.getCollectionType(umlxTypedElement.isIsOrdered(), umlxTypedElement.isIsUnique(), asClass, |
| umlxTypedElement.isIsNullFree(), null, null); |
| } |
| return asClass; |
| } |
| |
| /** |
| * Create a new trace for the given list of generated objects for the given |
| * context. |
| */ |
| protected void install(@NonNull UMLXElement umlxElement, @NonNull Element qvtrElement) { |
| Element oldQVTrElement = umlxElement2qvtrElement.put(umlxElement, qvtrElement); |
| assert oldQVTrElement == null; |
| for (@NonNull String comment : UMLXUtil.getComments(umlxElement)) { |
| qvtrElement.getOwnedComments().add(createComment(comment)); |
| } |
| } |
| |
| protected @NonNull OCLExpression parseContextualExpression(@NonNull EObject eContainer, @NonNull List<String> textExpression) throws CompilerChainException { |
| UMLXParserContext parserContext = new UMLXParserContext(environmentFactory, eContainer); |
| try { |
| StringBuilder s = new StringBuilder(); |
| for (String line : textExpression) { |
| if (s.length() > 0) { |
| s.append("\n"); |
| } |
| s.append(line); |
| } |
| OCLExpression asExpression = parserContext.parseExpression(eContainer, s.toString()); |
| PivotUtilInternal.resetContainer(asExpression); |
| return asExpression; |
| } catch (Exception e) { |
| throw new CompilerChainException(e); |
| } |
| } |
| |
| private void resolveTxQueryBody(@NonNull TxQueryNode txQueryNode) throws CompilerChainException { |
| Function asFunction = getQVTrElement(Function.class, txQueryNode); |
| List<String> lines = txQueryNode.getInitExpressionLines(); |
| if (lines.size() > 0) { |
| OCLExpression qvtrExpression = parseContextualExpression(asFunction, lines); |
| asFunction.setQueryExpression(qvtrExpression); |
| } |
| } |
| |
| public void transform() throws CompilerChainException { |
| for (EObject eObject : umlxResource.getContents()) { |
| if (eObject instanceof UMLXModel) { |
| RelationModel qvtrModel = QVTrelationFactory.eINSTANCE.createRelationModel(); |
| qvtrModel.setExternalURI(qvtrResource.getURI().toString()); |
| ((UMLXModel)eObject).accept(new CreateVisitor(this, qvtrModel)); |
| qvtrResource.getContents().add(qvtrModel); |
| } |
| } |
| for (@NonNull TxQueryNode txQueryNode : txQueryNodes) { |
| resolveTxQueryBody(txQueryNode); |
| } |
| for (@NonNull PatternForest patternForest : patternForests) { |
| patternForest.resolveExpressions(); // Pass 2: parse the OCL text |
| } |
| } |
| } |