/* * #%~ * UML2 Translator * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ide.plugins.uml2.vdm2uml; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.ui.PartInitException; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.LiteralString; import org.eclipse.uml2.uml.Model; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.TemplateParameter; import org.eclipse.uml2.uml.TemplateSignature; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.UMLPackage; import org.eclipse.uml2.uml.VisibilityKind; import org.eclipse.uml2.uml.resource.UMLResource; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.analysis.DepthFirstAnalysisAdaptorQuestion; import org.overture.ast.definitions.AClassClassDefinition; import org.overture.ast.definitions.AExplicitFunctionDefinition; import org.overture.ast.definitions.AExplicitOperationDefinition; import org.overture.ast.definitions.AInstanceVariableDefinition; import org.overture.ast.definitions.ATypeDefinition; import org.overture.ast.definitions.AValueDefinition; import org.overture.ast.definitions.PDefinition; import org.overture.ast.definitions.SClassDefinition; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.patterns.AIdentifierPattern; import org.overture.ast.patterns.PPattern; import org.overture.ast.types.AClassType; import org.overture.ast.types.AFunctionType; import org.overture.ast.types.AOperationType; import org.overture.ast.types.AParameterType; import org.overture.ast.types.AUnknownType; import org.overture.ast.types.PType; import org.overture.ide.plugins.uml2.UmlConsole; import org.overture.interpreter.assistant.pattern.PPatternAssistantInterpreter; public class Vdm2Uml { private static class ClassesMap extends HashMap<String, Class> { /** * */ private static final long serialVersionUID = 1L; @Override public String toString() { StringBuilder sb = new StringBuilder(); for (java.util.Map.Entry<String, Class> entry : entrySet()) { String prefix = entry.getKey(); while(prefix.length()<20) { prefix+=" "; } final Class value = entry.getValue(); sb.append(prefix+" -> "+ formatClass(value)+"\n"); for (Classifier nestedValue : value.getNestedClassifiers()) { prefix=""; while(prefix.length()<20) { prefix+=" "; } sb.append(prefix+ " -> "+formatClass(nestedValue)+"\n"); } } return sb.toString(); } private static String formatClass(Classifier nestedValue) { String name = nestedValue.getName(); while(name.length()<20) { name+=" "; } return name+"\t"+(nestedValue+"").substring(nestedValue.toString().indexOf('(')); } } private UmlConsole console = new UmlConsole(); UmlTypeCreator utc = new UmlTypeCreator(new UmlTypeCreator.ClassTypeLookup() { public Class lookup(AClassType type) { return lookup(type.getName().getName()); } @Override public Class lookup(String className) { return classes.get(className); } }, console); private Model modelWorkingCopy = null; private Map<String, Class> classes = new ClassesMap(); private boolean extendedAssociationMapping = false; private boolean deployArtifactsOutsideNodes = false; public Vdm2Uml(boolean preferAssociations, boolean deployArtifactsOutsideNodes) { extendedAssociationMapping = preferAssociations; this.deployArtifactsOutsideNodes = deployArtifactsOutsideNodes; } public Model getModel() { return this.modelWorkingCopy; } public Model convert(String name, List<SClassDefinition> classes) { try { console.show(); } catch (PartInitException e) { } console.out.println("#\n# Starting translation of project: " + name + "\n#"); console.out.println("# Properties:"); console.out.println("# \tPrefer associations: " + (extendedAssociationMapping ? "yes" : "no")); console.out.println("# \tDisable nested artifacts in deployment diagrams: " + (deployArtifactsOutsideNodes ? "yes" : "no")); // console.out.println("# Into: "+outputDir+"\n#"); console.out.println("-------------------------------------------------------------------------"); modelWorkingCopy = UMLFactory.eINSTANCE.createModel(); modelWorkingCopy.setName(name); utc.setModelWorkingCopy(modelWorkingCopy); List<SClassDefinition> onlyClasses = new Vector<SClassDefinition>(); onlyClasses.addAll(classes); for (SClassDefinition sClassDefinition : classes) { if (sClassDefinition instanceof AClassClassDefinition) { continue; } onlyClasses.remove(sClassDefinition); } buildUml(onlyClasses); new UmlDeploymentCreator(modelWorkingCopy, console, deployArtifactsOutsideNodes, utc).buildDeployment(classes); return modelWorkingCopy; } public void save(URI uri) throws IOException { console.out.println("Saving UML model to: " + uri.toFileString()+".uml"); Resource resource = new ResourceSetImpl().createResource(uri.appendFileExtension(UMLResource.FILE_EXTENSION)); resource.getContents().add(modelWorkingCopy); resource.save(null); // TODO: fix for modelio not supporting version 4.0.0 of EMF UML try { FileReader fr = new FileReader(uri.toFileString() + ".uml"); BufferedReader br = new BufferedReader(fr); String line = null; List<String> buffer = new Vector<String>(); while ((line = br.readLine()) != null) { line = line.replace("\"http://www.eclipse.org/uml2/4.0.0/UML\"", "\"http://www.eclipse.org/uml2/3.0.0/UML\""); buffer.add(line); } br.close(); FileWriter fw = new FileWriter(uri.toFileString() + ".uml"); PrintWriter out = new PrintWriter(fw); for (Iterator<String> iterator = buffer.iterator(); iterator.hasNext();) { out.write(iterator.next()); if (iterator.hasNext()) { out.println(); } } out.close(); } catch (IOException e) { } } private void buildUml(List<SClassDefinition> classes) { // Build class container for (SClassDefinition sClass : classes) { console.out.println("Converting class: " + sClass.getName()); String className = sClass.getName().getName(); Class class_ = buildClass(sClass); this.classes.put(className, class_); } // build inheritance relationship for (SClassDefinition sClass : classes) { Class thisClass = this.classes.get(sClass.getName().getName()); for (ILexNameToken superToken : sClass.getSupernames()) { console.out.println("Adding generalization between: " + thisClass.getName() + " -> " + superToken.getFullName()); Class superClass = this.classes.get(superToken.getName()); thisClass.createGeneralization(superClass); } } console.out.println("Converting types"); // Create types embedded in VDM classes for (SClassDefinition sClass : classes) { String className = sClass.getName().getName(); Class class_ = this.classes.get(className); addTypes(class_, sClass); } console.out.println("Converting remaining class definitions"); // Build operations, functions, instance variables and values for (SClassDefinition sClass : classes) { String className = sClass.getName().getName(); Class class_ = this.classes.get(className); addAttributesToClass(class_, sClass); } } private void addTypes(Class class_, SClassDefinition sClass) { console.out.println("Converting types for class: " + sClass.getName().getName()); for (PDefinition def : sClass.getDefinitions()) { if (def instanceof ATypeDefinition) { PType type = Vdm2UmlUtil.assistantFactory.createPDefinitionAssistant().getType(def); console.out.println("\tConverting type: " + type); utc.create(class_, type); } else { } } } private void addAttributesToClass(Class class_, SClassDefinition sClass) { console.out.println("Converting definitions for class: " + sClass.getName().getName()); for (PDefinition def : sClass.getDefinitions()) { if (def instanceof AInstanceVariableDefinition) { addInstanceVariableToClass(class_, (AInstanceVariableDefinition) def); } else if (def instanceof AExplicitOperationDefinition) { addExplicitOperationToClass(class_, (AExplicitOperationDefinition) def); } else if (def instanceof AExplicitFunctionDefinition) { addExplicitFunctionToClass(class_, (AExplicitFunctionDefinition) def); } else if (def instanceof AValueDefinition) { addValueToClass(class_, (AValueDefinition) def); } else { } } } private void addValueToClass(Class class_, AValueDefinition def) { String name = getDefName(def); PType defType = Vdm2UmlUtil.assistantFactory.createPDefinitionAssistant().getType(def); utc.create(class_, defType); Type umlType = utc.getUmlType(defType); if (Vdm2UmlUtil.assistantFactory.createPTypeAssistant().isClass(defType, null) && !(defType instanceof AUnknownType) && !extendedAssociationMapping || Vdm2UmlAssociationUtil.validType(defType) && extendedAssociationMapping) { console.out.println("\tAdding association for value: " + name); Vdm2UmlAssociationUtil.createAssociation(name, defType, def.getAccess(), def.getExpression(), classes, class_, true, utc); } else { console.out.println("\tAdding property for value: " + name); Property s = class_.createOwnedAttribute(name, umlType); s.setIsStatic(Vdm2UmlUtil.assistantFactory.createPAccessSpecifierAssistant().isStatic(def.getAccess())); s.setVisibility(Vdm2UmlUtil.convertAccessSpecifierToVisibility(def.getAccess())); s.setIsReadOnly(true); if (Vdm2UmlUtil.isOptional(defType)) { s.setLower(0); } if (def.getExpression() != null) { s.setDefault(def.getExpression().toString()); } } } private String getDefName(PDefinition def) { if (def instanceof AValueDefinition) { AValueDefinition valueDef = (AValueDefinition) def; PPattern expression = valueDef.getPattern(); if (expression instanceof AIdentifierPattern) { return ((AIdentifierPattern) expression).getName().getName(); } } else { return def.getName().getName(); } return "null"; } private static class TemplateParameterTypeCreator extends DepthFirstAnalysisAdaptorQuestion<TemplateParameterTypeCreator.OperationContext> { protected static class OperationContext { Class class_; Operation operation; public OperationContext(Class class_, Operation operation) { this.class_ = class_; this.operation = operation; } } TemplateSignature sig = null; Map<String, Classifier> templateParameters = new HashMap<String, Classifier>(); @Override public void caseAParameterType(AParameterType t, OperationContext question) throws AnalysisException { if (sig == null) { sig = question.operation.createOwnedTemplateSignature(UMLPackage.Literals.TEMPLATE_SIGNATURE); } /** * Modelio doesnt support Classifier template parameters so the lines: <br/> * TemplateParameter tp = sig.createOwnedParameter(UMLPackage.Literals.CLASSIFIER_TEMPLATE_PARAMETER);<br/> * Class sss = (Class) tp.createOwnedParameteredElement(UMLPackage.Literals.CLASS);<br/> * have been replaced with an alternative solution that it can import.<br/> * The lines:<br/> * LiteralString literalStringDefault =(LiteralString) * tp.createOwnedDefault(UMLPackage.Literals.LITERAL_STRING); * literalStringDefault.setName(UmlTypeCreatorBase.getName(t));<br/> * are also not needed but makes it look better in ModelioThe proper solution is described here: * http://www.eclipse.org/modeling/mdt/uml2/docs/ articles/Defining_Generics_with_UML_Templates/article.html */ String pName = UmlTypeCreatorBase.getName(t); if (!templateParameters.containsKey(pName)) { TemplateParameter tp = sig.createOwnedParameter(UMLPackage.Literals.TEMPLATE_PARAMETER); LiteralString literalStringDefault = (LiteralString) tp.createOwnedDefault(UMLPackage.Literals.LITERAL_STRING); literalStringDefault.setName(UmlTypeCreatorBase.getName(t)); Class sss = null; Object c = question.class_.getNestedClassifier(pName); if (c instanceof Class) { sss = (Class) c; } else { sss = (Class) question.class_.createNestedClassifier(pName, UMLPackage.Literals.CLASS); } sss.setName(pName); templateParameters.put(pName, sss); } // else sorry we only support unique template parameter names } @Override public void caseAClassClassDefinition(AClassClassDefinition node, OperationContext question) throws AnalysisException { // stop visiting childreb } public Map<String, Classifier> apply(List<PType> nodes, OperationContext question) throws AnalysisException { for (PType pType : nodes) { pType.apply(this, question); } return templateParameters; } public Map<String, Classifier> apply(PType node, OperationContext question) throws AnalysisException { Vector<PType> nodes = new Vector<PType>(); nodes.add(node); return apply(nodes, question); } } private void addExplicitFunctionToClass(Class class_, AExplicitFunctionDefinition def) { console.out.println("\tAdding function: " + def.getName().getName()); EList<String> names = new BasicEList<String>(); for (PPattern p : def.getParamPatternList().get(0)) { // HERE SEE: Downcast the assistantFactory here. Narrowing it to interpreter assistant. List<AIdentifierPattern> ids = Vdm2UmlUtil.assistantFactory.createPPatternAssistant().findIdentifiers(p); if (!ids.isEmpty()) { names.add(ids.get(0).toString()); } if (ids.size() > 1) { console.err.println("Some argument is in multiple parts: " + ids); } } EList<Type> types = new BasicEList<Type>(); AFunctionType type = (AFunctionType) def.getType(); Operation operation = class_.createOwnedOperation(def.getName().getName(), null, null, null); // Map<String, Classifier> templateParameters = new HashMap<String, Classifier>(); // TemplateSignature sig = null; // for (PType t : type.getParameters()) // { // if (t instanceof AParameterType) // { // if (sig == null) // { // sig = operation.createOwnedTemplateSignature(UMLPackage.Literals.TEMPLATE_SIGNATURE); // } // // /* // * Modelio doesnt support Classifier template parameters so the lines: // * <br/>TemplateParameter tp = // * sig.createOwnedParameter(UMLPackage.Literals.CLASSIFIER_TEMPLATE_PARAMETER);<br/>Class sss = (Class) // * tp.createOwnedParameteredElement(UMLPackage.Literals.CLASS);<br/>have been replaced with an alternative // * solution that it can import.<br/>The lines:<br/>LiteralString literalStringDefault =(LiteralString) // * tp.createOwnedDefault(UMLPackage.Literals.LITERAL_STRING); // * literalStringDefault.setName(UmlTypeCreatorBase.getName(t));<br/>are also not needed but makes it look // * better in ModelioThe proper solution is described here: // * http://www.eclipse.org/modeling/mdt/uml2/docs/ // * articles/Defining_Generics_with_UML_Templates/article.html // */ // TemplateParameter tp = sig.createOwnedParameter(UMLPackage.Literals.TEMPLATE_PARAMETER); // String pName = UmlTypeCreatorBase.getName(t); // // LiteralString literalStringDefault = (LiteralString) // tp.createOwnedDefault(UMLPackage.Literals.LITERAL_STRING); // literalStringDefault.setName(UmlTypeCreatorBase.getName(t)); // // Class sss = (Class) class_.createNestedClassifier(pName, UMLPackage.Literals.CLASS); // sss.setName(pName); // templateParameters.put(pName, sss); // } // } Map<String, Classifier> templateParameters = null; try { TemplateParameterTypeCreator tpCreator = new TemplateParameterTypeCreator(); TemplateParameterTypeCreator.OperationContext oCtxt = new TemplateParameterTypeCreator.OperationContext(class_, operation); templateParameters = tpCreator.apply(type.getParameters(), oCtxt); templateParameters = tpCreator.apply(type.getResult(), oCtxt); } catch (AnalysisException e) { console.err.println("An error occured during template parameter creation in: " + operation.getName()); e.printStackTrace(console.err); } utc.addTemplateTypes(templateParameters); for (PType t : type.getParameters()) { utc.create(class_, t); types.add(utc.getUmlType(t)); } utc.create(class_, type.getResult()); Type returnUmlType = utc.getUmlType(type.getResult()); utc.removeTemplateTypees(); operation.setType(returnUmlType); for (int i = 0; i < names.size(); i++) { operation.createOwnedParameter(names.get(i), types.get(i)); } // Operation operation = class_.createOwnedOperation(def.getName().name, names, types, returnUmlType); operation.setVisibility(Vdm2UmlUtil.convertAccessSpecifierToVisibility(def.getAccess())); operation.setIsStatic(Vdm2UmlUtil.assistantFactory.createPAccessSpecifierAssistant().isStatic(def.getAccess())); operation.setIsQuery(true); } private void addExplicitOperationToClass(Class class_, AExplicitOperationDefinition def) { console.out.println("\tAdding operation: " + def.getName().getName()); EList<String> names = new BasicEList<String>(); EList<Type> types = new BasicEList<Type>(); for (PPattern p : def.getParameterPatterns()) { // Downcast the assistantFactory here. Narrowing it to interpreter assistant. List<AIdentifierPattern> ids = ((PPatternAssistantInterpreter) Vdm2UmlUtil.assistantFactory.createPPatternAssistant()).findIdentifiers(p); if (!ids.isEmpty()) { String name = ids.get(0).toString(); names.add(name); // now find the type for (PDefinition d : def.getParamDefinitions()) { if (d.getName().getName().equals("self") || !d.getName().getName().equals(name)) { continue; } PType type = Vdm2UmlUtil.assistantFactory.createPDefinitionAssistant().getType(d); utc.create(class_, type); types.add(utc.getUmlType(type)); } if (names.size() != types.size()) { console.err.println("Missing type for argument \"" + name + "\" in " + def.getName()); } } if (ids.size() > 1) { console.err.println("Some argument is in multiple parts: " + ids); } } PType returnType = ((AOperationType) Vdm2UmlUtil.assistantFactory.createPDefinitionAssistant().getType(def)).getResult(); utc.create(class_, returnType); Type returnUmlType = utc.getUmlType(returnType); Operation operation = class_.createOwnedOperation(def.getName().getName(), names, types, returnUmlType); operation.setVisibility(Vdm2UmlUtil.convertAccessSpecifierToVisibility(def.getAccess())); operation.setIsStatic(Vdm2UmlUtil.assistantFactory.createPAccessSpecifierAssistant().isStatic(def.getAccess())); operation.setIsQuery(false); } private void addInstanceVariableToClass(Class class_, AInstanceVariableDefinition def) { String name = def.getName().getName(); PType defType = Vdm2UmlUtil.assistantFactory.createPDefinitionAssistant().getType(def); utc.create(class_, defType); Type type = utc.getUmlType(defType); if (Vdm2UmlUtil.assistantFactory.createPTypeAssistant().isClass(defType, null) && !(defType instanceof AUnknownType) && !extendedAssociationMapping || Vdm2UmlAssociationUtil.validType(defType) && extendedAssociationMapping) { console.out.println("\tAdding association for instance variable: " + def.getName().getName()); Vdm2UmlAssociationUtil.createAssociation(name, defType, def.getAccess(), def.getExpression(), classes, class_, false, utc); } else { console.out.println("\tAdding property for instance variable: " + def.getName().getName()); Property attribute = class_.createOwnedAttribute(name, type); attribute.setIsStatic(Vdm2UmlUtil.assistantFactory.createPAccessSpecifierAssistant().isStatic(def.getAccess())); attribute.setVisibility(Vdm2UmlUtil.convertAccessSpecifierToVisibility(def.getAccess())); if (Vdm2UmlUtil.isOptional(defType)) { attribute.setLower(0); } if (def.getExpression() != null) { attribute.setDefault(def.getExpression().toString()); } } } private Class buildClass(SClassDefinition sClass) { String name = sClass.getName().getName(); boolean isAbstract = Vdm2UmlUtil.hasSubclassResponsabilityDefinition(sClass.getDefinitions()); Class class_ = modelWorkingCopy.createOwnedClass(name, isAbstract); boolean isActive = Vdm2UmlUtil.isClassActive(sClass); class_.setIsActive(isActive); class_.setVisibility(VisibilityKind.PUBLIC_LITERAL); return class_; } }