package controller.merge.xmi.xclass; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; import uml2parser.ModelFileInfo; import uml2parser.XmiElement; import controller.comparer.xmi.XmiAssociationElement; import controller.comparer.xmi.XmiAttributeElement; import controller.comparer.xmi.XmiBaseElement; import controller.comparer.xmi.XmiClassDiagramComparer; import controller.comparer.xmi.XmiClassDiagramParser; import controller.comparer.xmi.XmiClassElement; import controller.comparer.xmi.XmiGeneralizationElement; import controller.comparer.xmi.XmiMemberEndElement; import controller.comparer.xmi.XmiOperationElement; import controller.comparer.xmi.XmiParameterElement; import controller.comparer.xmi.XmiValueElement; public class XmiMergeClassProcessor { // Tags private final static String PAPYRUS_UML_MODEL = "uml:Model"; private final static String PAPYRUS_NOTATION_DIAGRAM = "notation:Diagram"; private final static String PAPYRUS_PACKAGED_ELEM = "packagedElement"; private final static String PAPYRUS_OPERATION_ELEM = "ownedOperation"; private final static String PAPYRUS_PROPERTY_ELEM = "ownedAttribute"; private final static String PAPYRUS_PARAMETER_ELEM = "ownedParameter"; private final static String PAPYRUS_GENERALIZATION_ELEM = "generalization"; private final static String PAPYRUS_MEMBER_END = "ownedEnd"; private final static String PAPYRUS_LOWER_VALUE = "lowerValue"; private final static String PAPYRUS_UPPER_VALUE = "upperValue"; private final static String PAPYRUS_DEFAULT_VALUE = "defaultValue"; private final static String PAPYRUS_VALUE = "value"; // UML Types private final static String PAPYRUS_PACKAGE_TYPE_CLASS = "uml:Class"; private final static String PAPYRUS_PACKAGE_TYPE_INTERFACE = "uml:Interface"; private final static String PAPYRUS_PACKAGE_TYPE_PRIMITIVE = "uml:PrimitiveType"; private final static String PAPYRUS_PACKAGE_TYPE_ASSOCIATION = "uml:Association"; private final static String PAPYRUS_PACKAGE_TYPE_LITERNAL_INTEGER = "uml:LiteralInteger"; private final static String PAPYRUS_PACKAGE_TYPE_LITERNAL_UNLILMITED = "uml:LiteralUnlimitedNatural"; // Attributes private final static String PAPYRUS_XMI_ATTRIBUTE_TYPE = "xmi:type"; private final static String PAPYRUS_ATTRIBUTE_NAME = "name"; private final static String PAPYRUS_ATTRIBUTE_ID = "xmi:id"; private final static String PAPYRUS_ATTRIBUTE_TYPE = "type"; private final static String PAPYRUS_ATTRIBUTE_VISIBILITY = "visibility"; private final static String PAPYRUS_ATTRIBUTE_DIRECTION = "direction"; private final static String PAPYRUS_GENERALIZATION_DIRECTION = "general"; private final static String PAPYRUS_ATTRIBUTE_ASSOCIATION = "association"; private final static String PAPYRUS_ATTRIBUTE_VALUE = "value"; private final static String PAPYRUS_AGGREGATION = "aggregation"; private final static String PAPYRUS_XSI_NIL = "xsi:nil"; private final static String PAPYRUS_ATTRIBUTE_GENERAL = "general"; // Queue stores the notatinoElements that will be used by the notatino // processor to generation noation file. private LinkedList<XmiNotationElement> notationElements = new LinkedList<XmiNotationElement>(); // Generalization is built seperately and attached to the end of notationElements private LinkedList<XmiNotationElement> notationGeneral = new LinkedList<XmiNotationElement>(); private DocumentBuilderFactory docFactory = DocumentBuilderFactory .newInstance(); private DocumentBuilder docBuilder; private Document umlDoc; private Element umlRootElement; private String fileName; private String fileId; private String fileNotationId; private String fileNotationName; private HashMap<String, String> replaceClass2Id = new HashMap<String, String>(); private HashMap<String, String> mapGeneralToParent = new HashMap<String, String>(); private HashMap<String, String> mapParentToSource = new HashMap<String, String>(); private NotationData notationData; private XmiClassDiagramComparer comparer; public void Process(XmiClassDiagramComparer comparer) { this.comparer = comparer; try { Initialize(); } catch (ParserConfigurationException e) { e.printStackTrace(); } ArrayList<XmiMergedClass> mergedList = comparer.getSameClass(); for (XmiMergedClass mergedClass : mergedList) { createClass(mergedClass, umlDoc, umlRootElement); } for (XmiMergedAssociation mergedAssociation : comparer.getAssociationUml()) { createAssociation(mergedAssociation, umlDoc, umlRootElement); } setupFileInformation(); // Create notation data for (XmiNotationElement element : notationGeneral) { notationElements.addLast(element); } notationData = new NotationData( notationElements, mapGeneralToParent, mapParentToSource, fileName, fileId, fileNotationName, fileNotationId); } /** * Retrieves the uml andn notation file ids and names */ private void setupFileInformation() { XmiClassDiagramParser extractNameAndId; if (comparer.getClassDiagram1() != null) { extractNameAndId = comparer.getClassDiagram1(); } else { extractNameAndId = comparer.getClassDiagram2(); } fileName = extractNameAndId.getUmlFile().getFileNameNoExtension(); fileId = extractNameAndId.getUmlModelId(); XmiElement notationElement = extractNameAndId.getNotationFile() .findElementsByName(PAPYRUS_NOTATION_DIAGRAM).get(0); fileNotationId = notationElement .getAttributeValue(this.PAPYRUS_ATTRIBUTE_ID); fileNotationName = notationElement .getAttributeValue(this.PAPYRUS_ATTRIBUTE_NAME); } private void setupIdTracker() { for (XmiMergedClass element : comparer.getSameClass()) { if (element.getClass1() != null && element.getClass2() != null) { replaceClass2Id.put(element.getClass2().getId(), element.getClass1().getId()); } } } /** * Creates the initial file and header. Also invokes a method to obtain the * file names and ids for uml and notation files. * * @throws ParserConfigurationException */ private void Initialize() throws ParserConfigurationException { setupFileInformation(); setupIdTracker(); docBuilder = docFactory.newDocumentBuilder(); // UML Header umlDoc = docBuilder.newDocument(); umlRootElement = umlDoc.createElement("uml:Model"); umlRootElement.setAttribute("xmi:version", "20110701"); umlRootElement.setAttribute("xmlns:xmi", "http://www.omg.org/spec/XMI/20110701"); umlRootElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); umlRootElement.setAttribute("xmlns:uml", "http://www.eclipse.org/uml2/4.0.0/UML"); umlRootElement.setAttribute("name", fileName); umlRootElement.setAttribute("xmi:id", fileId); umlDoc.appendChild(umlRootElement); } /** * Creates the class element * * @param mergedClass * @param doc * @param rootElement */ private void createClass(XmiMergedClass mergedClass, Document doc, Element rootElement) { String newClassName = mergedClass.getNewName(); String classId; XmiClassElement class1 = mergedClass.getClass1(); XmiClassElement class2 = mergedClass.getClass2(); if (class1 != null) { classId = class1.getId(); // Add a notation element for notation file creation notationElements.addLast(new XmiNotationElement(class1.getId(), class1.getId(), comparer.getClassDiagram1() .getNotationFile(), XmiNotationElement.TYPE_CLASS)); } else if (class2 != null) { classId = class2.getId(); // Add a notation element for notation file creation notationElements.addLast(new XmiNotationElement(class2.getId(), class2.getId(), comparer.getClassDiagram2() .getNotationFile(), XmiNotationElement.TYPE_CLASS)); } else { classId = "NO ID"; } // Base element Element classElement = doc.createElement(PAPYRUS_PACKAGED_ELEM); rootElement.appendChild(classElement); classElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, PAPYRUS_PACKAGE_TYPE_CLASS); classElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, classId); classElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, newClassName); // create attributes if (mergedClass.getAttributes() != null) { for (XmiAttributeElement attributeElement : mergedClass .getAttributes()) { appendAttribute(attributeElement, doc, classElement); // Add a notation element for notation file creation notationElements.getLast().addElement( new XmiNotationElement(attributeElement.getId(), attributeElement.getId(), comparer .getClassDiagram1().getNotationFile(), XmiNotationElement.TYPE_ATTRIBUTE)); } } if (mergedClass.getAttributes2() != null) { ArrayList<XmiAttributeElement> att2 = mergedClass.getAttributes2(); // Remove attributes that were already added from class1 if (mergedClass.getAttributes() != null) { att2.removeAll(mergedClass.getAttributes()); } for (XmiAttributeElement attributeElement : att2) { appendAttribute(attributeElement, doc, classElement); // Add a notation element for notation file creation notationElements.getLast().addElement( new XmiNotationElement(attributeElement.getId(), attributeElement.getId(), comparer .getClassDiagram2().getNotationFile(), XmiNotationElement.TYPE_ATTRIBUTE)); } } // Create operations if (mergedClass.getOperations() != null) { for (XmiOperationElement operationElement : mergedClass .getOperations()) { appendOperation(operationElement, doc, classElement); notationElements.getLast().addElement( new XmiNotationElement(operationElement.getId(), operationElement.getId(), comparer .getClassDiagram1().getNotationFile(), XmiNotationElement.TYPE_OPERATION)); } } if (mergedClass.getOperations2() != null) { // Remove operations that were already added from class1 ArrayList<XmiOperationElement> op = mergedClass.getOperations2(); if (mergedClass.getAttributes() != null) { op.removeAll(mergedClass.getAttributes()); } for (XmiOperationElement operationElement : (ArrayList<XmiOperationElement>) op) { appendOperation(operationElement, doc, classElement); notationElements.getLast().addElement( new XmiNotationElement(operationElement.getId(), operationElement.getId(), comparer .getClassDiagram2().getNotationFile(), XmiNotationElement.TYPE_OPERATION)); } } // Create generalization notation elements if (mergedClass.getGeneralizations() != null) { for (XmiGeneralizationElement generalization : mergedClass .getGeneralizations()) { appendGeneralization(generalization, doc, classElement, classId); // Add a notation element for notation file creation notationGeneral.addLast(new XmiNotationElement(generalization .getId(), generalization.getId(), comparer .getClassDiagram1().getNotationFile(), XmiNotationElement.TYPE_GENERALIZATION)); } } if (mergedClass.getGeneralizations2() != null) { // Remove generalizations that were already added from class1 ArrayList<XmiGeneralizationElement> gen = mergedClass .getGeneralizations2(); if (mergedClass.getGeneralizations() != null) { gen.removeAll(mergedClass.getGeneralizations()); } for (XmiGeneralizationElement generalization : gen) { appendGeneralization(generalization, doc, classElement, classId); // Add a notation element for notation file creation notationGeneral.addLast(new XmiNotationElement(generalization .getId(), generalization.getId(), comparer .getClassDiagram2().getNotationFile(), XmiNotationElement.TYPE_GENERALIZATION)); } } } /** * Append the attribute Node * * @param attribute * @param doc * @param rootElement */ private void appendAttribute(XmiAttributeElement attribute, Document doc, Element rootElement) { // Base element Element attributeElement = doc.createElement(PAPYRUS_PROPERTY_ELEM); rootElement.appendChild(attributeElement); // If the attribute value is doesn't exist or has the default <DEFAULT> // value, don't // add it to the XML. if (attribute.getUmlType() == null) { if (!attribute.getUmlType().equals(XmiBaseElement.DEFAULT_TYPE)) { attributeElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, attribute.getUmlType()); } } attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, attribute.getId()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, attribute.getName()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_VISIBILITY, attribute.getVisibility()); if (attribute.getLowerValue() != null) { appendLowerValue(attribute.getLowerValue(), doc, attributeElement); } if (attribute.getUpperValue() != null) { appendUpperValue(attribute.getUpperValue(), doc, attributeElement); } if (attribute.getDefaultValue() != null) { appendDefaultValue(attribute.getDefaultValue(), doc, attributeElement); } } /** * Append LowerValue node * * @param element * @param doc * @param rootElement */ private void appendLowerValue(XmiValueElement element, Document doc, Element rootElement) { Element attributeElement = doc.createElement(PAPYRUS_LOWER_VALUE); rootElement.appendChild(attributeElement); attributeElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, element.getUmlType()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, element.getId()); if (element.getValue() != null) { if (!element.getValue().isEmpty()) { attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_VALUE, element.getValue()); } } } private void appendUpperValue(XmiValueElement element, Document doc, Element rootElement) { Element attributeElement = doc.createElement(PAPYRUS_UPPER_VALUE); rootElement.appendChild(attributeElement); attributeElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, element.getUmlType()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, element.getId()); if (element.getValue() != null) { if (!element.getValue().isEmpty()) { attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_VALUE, element.getValue()); } } } private void appendDefaultValue(XmiValueElement element, Document doc, Element rootElement) { Element attributeElement = doc.createElement(PAPYRUS_DEFAULT_VALUE); rootElement.appendChild(attributeElement); attributeElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, element.getUmlType()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, element.getId()); if (element.isNull()) { Element nilElement = doc.createElement(PAPYRUS_DEFAULT_VALUE); attributeElement.appendChild(nilElement); nilElement.setAttribute(PAPYRUS_XSI_NIL, "true"); } else { attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_VALUE, element.getValue()); attributeElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, element.getName()); } } /** * Append operation node * * @param operation * @param doc * @param rootElement */ private void appendOperation(XmiOperationElement operation, Document doc, Element rootElement) { // Base element Element operationElement = doc.createElement(PAPYRUS_OPERATION_ELEM); rootElement.appendChild(operationElement); // Only add the "type" attribute if it exist and not the default blank // value // Papyrus will throw an error if the type shouldn't exist. if (operation.getUmlType() != null) { if (!operation.getUmlType().equals(XmiBaseElement.DEFAULT_TYPE)) { operationElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, operation.getUmlType()); } } operationElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, operation.getId()); operationElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, operation.getName()); operationElement.setAttribute(PAPYRUS_ATTRIBUTE_VISIBILITY, operation.getVisibility()); for (XmiParameterElement parameter : operation.getParameters()) { appendParameter(parameter, doc, operationElement); } } /** * Append parameter node * * @param element * @param doc * @param rootElement */ private void appendParameter(XmiParameterElement element, Document doc, Element rootElement) { Element paramElement = doc.createElement(PAPYRUS_PARAMETER_ELEM); rootElement.appendChild(paramElement); // Only add the "type" attribute if it exist and not the default blank // value // Papyrus will throw an error if the type shouldn't exist. if (element.getUmlType() != null) { if (!element.getUmlType().equals(XmiBaseElement.DEFAULT_TYPE)) { paramElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, element.getUmlType()); } } // Only add the "direction" attribute if it exist, not "in" and not // empty if (element.getDirection() != null) { if (!element.getDirection() .equals(XmiParameterElement.DIRECTION_IN) && !element.getDirection().isEmpty()) { paramElement.setAttribute(PAPYRUS_ATTRIBUTE_DIRECTION, element.getDirection()); } } paramElement.setAttribute(PAPYRUS_ATTRIBUTE_VISIBILITY, element.getVisibility()); paramElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, element.getName()); paramElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, element.getId()); } /** * Append operation node * * @param generalization * @param doc * @param rootElement */ private void appendGeneralization(XmiGeneralizationElement generalization, Document doc, Element rootElement, String classId) { // Base element Element operationElement = doc .createElement(PAPYRUS_GENERALIZATION_ELEM); rootElement.appendChild(operationElement); operationElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, generalization.getId()); String parentId; if (replaceClass2Id.containsKey(generalization.getParent())) { parentId = replaceClass2Id.get(generalization.getParent()); } else { parentId = generalization.getParent(); } operationElement.setAttribute(PAPYRUS_ATTRIBUTE_GENERAL, parentId); mapGeneralToParent.put(generalization.getId(), parentId); mapParentToSource.put(generalization.getId(), classId); } /** * create association node * * @param generalization * @param doc * @param rootElement */ private void createAssociation(XmiMergedAssociation mergedAssociation, Document doc, Element rootElement) { XmiAssociationElement association = mergedAssociation.getAssociationElement(); String class1Id = mergedAssociation.getClass1Id(); String class2Id = mergedAssociation.getClass2Id(); String class1Name = mergedAssociation.getClass1Name(); String class2Name = mergedAssociation.getClass2Name(); ModelFileInfo diagram; if (mergedAssociation.getDiagramnum() == 1) { diagram = comparer.getClassDiagram1().getNotationFile(); } else { diagram = comparer.getClassDiagram2().getNotationFile(); } // Base element Element associationElement = doc.createElement(PAPYRUS_PACKAGED_ELEM); rootElement.appendChild(associationElement); String source = null; String target = null; // Association only set up for non-classifier which has 2 member ends if (association.getMemberEnds().size() == 2) { appendOwnedEnd(association.getMemberEnds().get(0), doc, associationElement, class1Id, class1Name); appendOwnedEnd(association.getMemberEnds().get(1), doc, associationElement, class2Id, class2Name); source = association.getMemberEnds().get(0).getId(); target = association.getMemberEnds().get(1).getId(); } associationElement.setAttribute(PAPYRUS_XMI_ATTRIBUTE_TYPE, PAPYRUS_PACKAGE_TYPE_ASSOCIATION); associationElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, association.getId()); associationElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, association.getName()); if (source != null && target != null) { associationElement.setAttribute(PAPYRUS_MEMBER_END, source + " " + target); } XmiNotationElement notationElement = new XmiNotationElement(association.getId(), association.getId(), diagram, XmiNotationElement.TYPE_ASSOCIATION); notationElement.setSource(class1Id); notationElement.setTarget(class2Id); notationElements.addLast(notationElement); } /** * Append OwnedEnd node * * @param element * @param doc * @param rootElement * @param typeId of the class at the end of the association * @param name is referring to the end of the association */ private void appendOwnedEnd(XmiMemberEndElement element, Document doc, Element rootElement, String typeId, String name) { Element ownedEndElement = doc.createElement(PAPYRUS_MEMBER_END); rootElement.appendChild(ownedEndElement); ownedEndElement.setAttribute(PAPYRUS_ATTRIBUTE_ID, element.getId()); ownedEndElement.setAttribute(PAPYRUS_ATTRIBUTE_TYPE, typeId); ownedEndElement.setAttribute(PAPYRUS_ATTRIBUTE_NAME, name); ownedEndElement.setAttribute(PAPYRUS_ATTRIBUTE_ASSOCIATION, element.getAssociationId()); if (element.getAggregation() != null) { if (!element.getAggregation().toString().isEmpty()) { ownedEndElement.setAttribute(PAPYRUS_AGGREGATION, element.getAggregation().name().toLowerCase()); } } if (element.getLowerValue() != null) { appendLowerValue(element.getLowerValue(), doc, ownedEndElement); } if (element.getUpperValue() != null) { appendUpperValue(element.getUpperValue(), doc, ownedEndElement); } } public String getFileName() { return this.fileName; } public String getNotationFileName() { return this.fileNotationName; } public String getNotationFileId() { return this.fileNotationId; } public NotationData getNotationData() { return this.notationData; } /** * Generate the uml file * * @param fileName */ public File GenerateFile(String fileName) { try { // write the content into xml file TransformerFactory transformerFactory = TransformerFactory .newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(umlDoc); File file = new File("C:\\temp\\" + fileName + ".uml"); StreamResult result = new StreamResult(file); System.out.println("uml file created!"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(source, result); return file; } catch (TransformerException e) { System.out.println("Failed uml file"); e.printStackTrace(); } return null; } }