Java tutorial
/** * This program and the accompanying materials * are made available under the terms of the License * which accompanies this distribution in the file LICENSE.txt */ package org.opengroup.archimate.xmlexchange; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.eclipse.emf.ecore.EObject; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.Namespace; import com.archimatetool.editor.ui.ColorFactory; import com.archimatetool.editor.ui.FontFactory; import com.archimatetool.editor.utils.StringUtils; import com.archimatetool.jdom.JDOMUtils; import com.archimatetool.model.IArchimateComponent; import com.archimatetool.model.IArchimateDiagramModel; import com.archimatetool.model.IArchimateElement; import com.archimatetool.model.IArchimateFactory; import com.archimatetool.model.IArchimateModel; import com.archimatetool.model.IBounds; import com.archimatetool.model.IDiagramModelArchimateConnection; import com.archimatetool.model.IDiagramModelArchimateObject; import com.archimatetool.model.IDiagramModelBendpoint; import com.archimatetool.model.IDiagramModelConnection; import com.archimatetool.model.IDiagramModelContainer; import com.archimatetool.model.IDiagramModelGroup; import com.archimatetool.model.IDiagramModelNote; import com.archimatetool.model.IDiagramModelObject; import com.archimatetool.model.IFontAttribute; import com.archimatetool.model.IProperties; import com.archimatetool.model.IProperty; import com.archimatetool.model.IRelationship; import com.archimatetool.model.util.ArchimateModelUtils; /** * XML Model Importer * * @author Phillip Beauvoir */ @SuppressWarnings("nls") public class XMLModelImporter implements IXMLExchangeGlobals { private IArchimateModel fModel; // Properties private Map<String, String> fPropertyDefinitionsList; public IArchimateModel createArchiMateModel(File instanceFile) throws IOException, JDOMException, XMLModelParserException { // Create a new Archimate Model and set its defaults fModel = IArchimateFactory.eINSTANCE.createArchimateModel(); fModel.setDefaults(); // Read file without Schema validation Document doc = JDOMUtils.readXMLFile(instanceFile); Element rootElement = doc.getRootElement(); // Parse Property Definitions first parsePropertyDefinitions(rootElement.getChild(ELEMENT_PROPERTYDEFS, OPEN_GROUP_NAMESPACE)); // Parse Root Element parseRootElement(rootElement); // Parse ArchiMate Elements parseArchiMateElements(rootElement.getChild(ELEMENT_ELEMENTS, OPEN_GROUP_NAMESPACE)); // Parse ArchiMate Relations parseArchiMateRelations(rootElement.getChild(ELEMENT_RELATIONSHIPS, OPEN_GROUP_NAMESPACE)); // Parse Views parseViews(rootElement.getChild(ELEMENT_VIEWS, OPEN_GROUP_NAMESPACE)); // TODO Parse Organization - not implemented as yet. // parseOrganization(rootElement.getChild(ELEMENT_ORGANIZATION, OPEN_GROUP_NAMESPACE)); return fModel; } // ========================================= Property Definitions ====================================== private void parsePropertyDefinitions(Element propertydefsElement) { if (propertydefsElement == null) { return; } fPropertyDefinitionsList = null; fPropertyDefinitionsList = new HashMap<String, String>(); // Archi only supports String types so we can ignore the data type for (Element propertyDefElement : propertydefsElement.getChildren(ELEMENT_PROPERTYDEF, OPEN_GROUP_NAMESPACE)) { String identifier = propertyDefElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); String name = propertyDefElement.getAttributeValue(ATTRIBUTE_NAME); if (identifier != null && name != null) { fPropertyDefinitionsList.put(identifier, name); } } } // ========================================= Root Element ====================================== private void parseRootElement(Element rootElement) { // Identifier String id = rootElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); if (id != null) { fModel.setId(id); } // Name String name = getChildElementText(rootElement, ELEMENT_NAME, true); if (name != null) { fModel.setName(name); } // Documentation String documentation = getChildElementText(rootElement, ELEMENT_DOCUMENTATION, false); if (documentation != null) { fModel.setPurpose(documentation); } // Properties addProperties(fModel, rootElement); } // ========================================= Properties ====================================== private void addProperties(IProperties propertiesModel, Element parentElement) { Element propertiesElement = parentElement.getChild(ELEMENT_PROPERTIES, OPEN_GROUP_NAMESPACE); if (propertiesElement != null) { for (Element propertyElement : propertiesElement.getChildren(ELEMENT_PROPERTY, OPEN_GROUP_NAMESPACE)) { String idref = propertyElement.getAttributeValue(ATTRIBUTE_IDENTIFIERREF); // Ignore special junction types if (PROPERTY_JUNCTION_ID.equals(idref)) { continue; } if (idref != null) { String propertyName = fPropertyDefinitionsList.get(idref); if (propertyName != null) { String propertyValue = getChildElementText(propertyElement, ELEMENT_VALUE, true); IProperty property = IArchimateFactory.eINSTANCE.createProperty(); property.setKey(propertyName); property.setValue(propertyValue); propertiesModel.getProperties().add(property); } } } } } // ========================================= Elements ====================================== private void parseArchiMateElements(Element elementsElement) throws XMLModelParserException { if (elementsElement == null) { throw new XMLModelParserException("No Elements found"); } for (Element childElement : elementsElement.getChildren(ELEMENT_ELEMENT, OPEN_GROUP_NAMESPACE)) { String type = childElement.getAttributeValue(ATTRIBUTE_TYPE, XSI_NAMESPACE); // If type is bogus ignore if (type == null) { continue; } // Junctions are a special case, so we look for a property if ("Junction".equals(type)) { type = getJunctionType(childElement); } IArchimateElement element = (IArchimateElement) XMLTypeMapper.createArchimateComponent(type); // If element is null throw exception if (element == null) { throw new XMLModelParserException("Element for type: " + type + " not found."); } // Identifier first String id = childElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); if (id != null) { element.setId(id); } // Add to model fModel.getDefaultFolderForElement(element).getElements().add(element); String name = getChildElementText(childElement, ELEMENT_LABEL, true); if (name != null) { element.setName(name); } String documentation = getChildElementText(childElement, ELEMENT_DOCUMENTATION, false); if (documentation != null) { element.setDocumentation(documentation); } // Properties addProperties(element, childElement); } } /** * Get the actual Junction type based on a property */ private String getJunctionType(Element element) { String junctionType = "Junction"; Element propertiesElement = element.getChild(ELEMENT_PROPERTIES, OPEN_GROUP_NAMESPACE); if (propertiesElement != null) { for (Element propertyElement : propertiesElement.getChildren(ELEMENT_PROPERTY, OPEN_GROUP_NAMESPACE)) { String idref = propertyElement.getAttributeValue(ATTRIBUTE_IDENTIFIERREF); if (PROPERTY_JUNCTION_ID.equals(idref)) { String propertyValue = getChildElementText(propertyElement, ELEMENT_VALUE, true); if (PROPERTY_JUNCTION_AND.equals(propertyValue)) { junctionType = "AndJunction"; } else if (PROPERTY_JUNCTION_OR.equals(propertyValue)) { junctionType = "OrJunction"; } break; } } } return junctionType; } // ========================================= Relations ====================================== private void parseArchiMateRelations(Element relationsElement) throws IOException { if (relationsElement == null) { // Optional return; } for (Element childElement : relationsElement.getChildren(ELEMENT_RELATIONSHIP, OPEN_GROUP_NAMESPACE)) { String type = childElement.getAttributeValue(ATTRIBUTE_TYPE, XSI_NAMESPACE); // If type is bogus ignore if (type == null) { continue; } IRelationship relation = (IRelationship) XMLTypeMapper.createArchimateComponent(type); // If relation is null throw exception if (relation == null) { throw new IOException("Relation for type: " + type + " not found."); } // Identifier first String id = childElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); if (id != null) { relation.setId(id); } // Source and target String sourceID = childElement.getAttributeValue(ATTRIBUTE_SOURCE); String targetID = childElement.getAttributeValue(ATTRIBUTE_TARGET); EObject eObjectSrc = ArchimateModelUtils.getObjectByID(fModel, sourceID); if (!(eObjectSrc instanceof IArchimateElement)) { throw new IOException("Source Element not found for id: " + sourceID); } EObject eObjectTgt = ArchimateModelUtils.getObjectByID(fModel, targetID); if (!(eObjectTgt instanceof IArchimateElement)) { throw new IOException("Target Element not found for id: " + targetID); } relation.setSource((IArchimateElement) eObjectSrc); relation.setTarget((IArchimateElement) eObjectTgt); // Add to model fModel.getDefaultFolderForElement(relation).getElements().add(relation); String name = getChildElementText(childElement, ELEMENT_LABEL, true); if (name != null) { relation.setName(name); } String documentation = getChildElementText(childElement, ELEMENT_DOCUMENTATION, false); if (documentation != null) { relation.setDocumentation(documentation); } // Properties addProperties(relation, childElement); } } // ========================================= Organization ====================================== @SuppressWarnings("unused") private void parseOrganization(Element organizationElement) { if (organizationElement == null) { // Optional return; } for (Element childElement : organizationElement.getChildren(ELEMENT_ITEM, OPEN_GROUP_NAMESPACE)) { parseItem(childElement); } } private void parseItem(Element itemElement) { // The idea is to see if we can match any referenced elements/relations into a suitable folder // and then move them to that folder. At this stage, it's not worth it. String idref = itemElement.getAttributeValue(ATTRIBUTE_IDENTIFIERREF); if (idref != null) { EObject eObject = ArchimateModelUtils.getObjectByID(fModel, idref); if (eObject instanceof IArchimateComponent) { } } // Folder? else { } for (Element childElement : itemElement.getChildren(ELEMENT_ITEM, OPEN_GROUP_NAMESPACE)) { parseItem(childElement); } } // ========================================= Views ====================================== private void parseViews(Element viewsElement) throws XMLModelParserException { if (viewsElement == null) { // Optional return; } for (Element viewElement : viewsElement.getChildren(ELEMENT_VIEW, OPEN_GROUP_NAMESPACE)) { IArchimateDiagramModel dm = IArchimateFactory.eINSTANCE.createArchimateDiagramModel(); fModel.getDefaultFolderForElement(dm).getElements().add(dm); // Identifier first String id = viewElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); if (id != null) { dm.setId(id); } // Viewpoint String viewPointName = viewElement.getAttributeValue(ATTRIBUTE_VIEWPOINT); if (viewPointName != null) { int viewPointID = XMLTypeMapper.getViewpointID(viewPointName); dm.setViewpoint(viewPointID); } // Name String name = getChildElementText(viewElement, ELEMENT_LABEL, true); if (name != null) { dm.setName(name); } // Documentation String documentation = getChildElementText(viewElement, ELEMENT_DOCUMENTATION, false); if (documentation != null) { dm.setDocumentation(documentation); } // Properties addProperties(dm, viewElement); // Nodes addNodes(dm, viewElement); // Connections addConnections(viewElement); } } // ========================================= Nodes ====================================== private void addNodes(IDiagramModelContainer parentContainer, Element parentElement) throws XMLModelParserException { for (Element nodeElement : parentElement.getChildren(ELEMENT_NODE, OPEN_GROUP_NAMESPACE)) { IDiagramModelObject dmo = null; // This has an element ref so it's an ArchiMate element node String elementRef = nodeElement.getAttributeValue(ATTRIBUTE_ELEMENTREF); if (hasValue(elementRef)) { EObject eObject = ArchimateModelUtils.getObjectByID(fModel, elementRef); if (!(eObject instanceof IArchimateElement)) { throw new XMLModelParserException("Element not found for id: " + elementRef); } // Create new diagram node object IArchimateElement element = (IArchimateElement) eObject; dmo = IArchimateFactory.eINSTANCE.createDiagramModelArchimateObject(); ((IDiagramModelArchimateObject) dmo).setArchimateElement(element); } // No element ref so this is another type of node, but what is it? else { boolean isGroup = NODE_TYPE_GROUP.equals(nodeElement.getAttributeValue(ATTRIBUTE_TYPE)); //boolean isNote = NODE_TYPE_TEXT.equals(nodeElement.getAttributeValue(ATTRIBUTE_TYPE)); // Does the graphical node have children? // Our notes cannot contain children, so if it does contain children it has to be a Group. boolean hasChildren = nodeElement.getChildren(ELEMENT_NODE, OPEN_GROUP_NAMESPACE).size() > 0; if (isGroup || hasChildren) { IDiagramModelGroup group = IArchimateFactory.eINSTANCE.createDiagramModelGroup(); dmo = group; // Name String name = getChildElementText(nodeElement, ELEMENT_LABEL, true); if (name != null) { dmo.setName(name); } // Documentation String documentation = getChildElementText(nodeElement, ELEMENT_DOCUMENTATION, false); if (documentation != null) { group.setDocumentation(documentation); } // Properties addProperties(group, nodeElement); } // A Note is our only other option else { IDiagramModelNote note = IArchimateFactory.eINSTANCE.createDiagramModelNote(); note.setBorderType(IDiagramModelNote.BORDER_RECTANGLE); dmo = note; // Text String text = getChildElementText(nodeElement, ELEMENT_LABEL, false); if (text != null) { note.setContent(text); } } } if (dmo != null) { // Add Identifier before adding to model String identifier = nodeElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); dmo.setId(identifier); // Add the child first parentContainer.getChildren().add(dmo); // Get the absolute bounds as declared in the XML file IBounds absoluteBounds = getNodeBounds(nodeElement); // Now convert the given absolute bounds into relative bounds IBounds relativeBounds = XMLExchangeUtils.convertAbsoluteToRelativeBounds(absoluteBounds, dmo); dmo.setBounds(relativeBounds); // Style addNodeStyle(dmo, nodeElement.getChild(ELEMENT_STYLE, OPEN_GROUP_NAMESPACE)); // Child nodes if (dmo instanceof IDiagramModelContainer) { addNodes((IDiagramModelContainer) dmo, nodeElement); } } } } /** * Get the object bounds as declared in XML. The x, y will be absolute values. */ IBounds getNodeBounds(Element nodeElement) throws XMLModelParserException { // Check for x, y, width and height String xString = nodeElement.getAttributeValue(ATTRIBUTE_X); String yString = nodeElement.getAttributeValue(ATTRIBUTE_Y); String wString = nodeElement.getAttributeValue(ATTRIBUTE_WIDTH); String hString = nodeElement.getAttributeValue(ATTRIBUTE_HEIGHT); if (!hasValue(xString) || !hasValue(yString) || !hasValue(wString) || !hasValue(hString)) { throw new XMLModelParserException("Co-ordinate value not found"); } int x = Integer.valueOf(xString); int y = Integer.valueOf(yString); int width = Integer.valueOf(wString); int height = Integer.valueOf(hString); return IArchimateFactory.eINSTANCE.createBounds(x, y, width, height); } /** * Node Style */ private void addNodeStyle(IDiagramModelObject dmo, Element styleElement) throws XMLModelParserException { if (styleElement == null) { return; } // Fill Color dmo.setFillColor(getRGBColorString(styleElement.getChild(ELEMENT_FILLCOLOR, OPEN_GROUP_NAMESPACE))); // Line Color dmo.setLineColor(getRGBColorString(styleElement.getChild(ELEMENT_LINECOLOR, OPEN_GROUP_NAMESPACE))); // Font addFont(dmo, styleElement.getChild(ELEMENT_FONT, OPEN_GROUP_NAMESPACE)); } // ======================================= Connections ==================================== private void addConnections(Element viewElement) throws XMLModelParserException { for (Element connectionElement : viewElement.getChildren(ELEMENT_CONNECTION, OPEN_GROUP_NAMESPACE)) { IDiagramModelConnection connection = null; // Get source node String sourceRef = connectionElement.getAttributeValue(ATTRIBUTE_SOURCE); EObject eObjectSourceNode = ArchimateModelUtils.getObjectByID(fModel, sourceRef); if (eObjectSourceNode == null) { throw new XMLModelParserException("Source node not found for id: " + sourceRef); } // Get target node String targetRef = connectionElement.getAttributeValue(ATTRIBUTE_TARGET); EObject eObjectTargetNode = ArchimateModelUtils.getObjectByID(fModel, targetRef); if (eObjectTargetNode == null) { throw new XMLModelParserException("Target node not found for id: " + targetRef); } // An ArchiMate relationship connection String relationshipRef = connectionElement.getAttributeValue(ATTRIBUTE_RELATIONSHIPREF); if (hasValue(relationshipRef)) { // Must be ArchiMate type source node if (!(eObjectSourceNode instanceof IDiagramModelArchimateObject)) { throw new XMLModelParserException("Source node is not an ArchiMate node for id: " + sourceRef); } // Must be ArchiMate type target node if (!(eObjectTargetNode instanceof IDiagramModelArchimateObject)) { throw new XMLModelParserException("Target node is not an ArchiMate node for id: " + targetRef); } // Get relationship EObject eObjectRelationship = ArchimateModelUtils.getObjectByID(fModel, relationshipRef); if (!(eObjectRelationship instanceof IRelationship)) { throw new XMLModelParserException("Relationship not found for id: " + relationshipRef); } // Create new ArchiMate connection with relationship connection = IArchimateFactory.eINSTANCE.createDiagramModelArchimateConnection(); ((IDiagramModelArchimateConnection) connection) .setRelationship((IRelationship) eObjectRelationship); } // Another connection type else { // Only connect notes and groups if (eObjectTargetNode instanceof IDiagramModelArchimateObject && eObjectSourceNode instanceof IDiagramModelArchimateObject) { continue; } // Create new ordinary connection connection = IArchimateFactory.eINSTANCE.createDiagramModelConnection(); } if (connection != null) { // Add Identifier before adding to model String identifier = connectionElement.getAttributeValue(ATTRIBUTE_IDENTIFIER); connection.setId(identifier); // Connect connection.connect((IDiagramModelObject) eObjectSourceNode, (IDiagramModelObject) eObjectTargetNode); // Bendpoints addBendpoints(connection, connectionElement); // Style addConnectionStyle(connection, connectionElement.getChild(ELEMENT_STYLE, OPEN_GROUP_NAMESPACE)); } } } /** * Add bendpoints */ private void addBendpoints(IDiagramModelConnection connection, Element connectionElement) throws XMLModelParserException { for (Element bendpointElement : connectionElement.getChildren(ELEMENT_BENDPOINT, OPEN_GROUP_NAMESPACE)) { String xString = bendpointElement.getAttributeValue(ATTRIBUTE_X); String yString = bendpointElement.getAttributeValue(ATTRIBUTE_Y); if (!hasValue(xString) || !hasValue(yString)) { throw new XMLModelParserException("Bendpoint co-ordinate value not found"); } int x = Integer.valueOf(xString); int y = Integer.valueOf(yString); IDiagramModelBendpoint bendpoint = IArchimateFactory.eINSTANCE.createDiagramModelBendpoint(); connection.getBendpoints().add(bendpoint); IBounds srcBounds = XMLExchangeUtils.getAbsoluteBounds(connection.getSource()); IBounds tgtBounds = XMLExchangeUtils.getAbsoluteBounds(connection.getTarget()); int startX = x - (srcBounds.getX() + (srcBounds.getWidth() / 2)); int startY = y - (srcBounds.getY() + (srcBounds.getHeight() / 2)); bendpoint.setStartX(startX); bendpoint.setStartY(startY); int endX = x - (tgtBounds.getX() + (tgtBounds.getWidth() / 2)); int endY = y - (tgtBounds.getY() + (tgtBounds.getHeight() / 2)); bendpoint.setEndX(endX); bendpoint.setEndY(endY); } } /** * Connection Style */ private void addConnectionStyle(IDiagramModelConnection connection, Element styleElement) throws XMLModelParserException { if (styleElement == null) { return; } // Line width String lineWidthString = styleElement.getAttributeValue(ATTRIBUTE_LINEWIDTH); if (hasValue(lineWidthString)) { int width = Integer.valueOf(lineWidthString); if (width < 0) { width = 1; } if (width > 3) { width = 3; } connection.setLineWidth(width); } // Line Color connection.setLineColor(getRGBColorString(styleElement.getChild(ELEMENT_LINECOLOR, OPEN_GROUP_NAMESPACE))); // Font addFont(connection, styleElement.getChild(ELEMENT_FONT, OPEN_GROUP_NAMESPACE)); } // ========================================= Helpers ====================================== private void addFont(IFontAttribute fontObject, Element fontElement) throws XMLModelParserException { if (fontElement == null) { return; } FontData newFontData = new FontData(FontFactory.getDefaultUserViewFontData().toString()); String fontName = fontElement.getAttributeValue(ATTRIBUTE_FONTNAME); if (hasValue(fontName)) { newFontData.setName(fontName); } String fontSize = fontElement.getAttributeValue(ATTRIBUTE_FONTSIZE); if (hasValue(fontSize)) { int val = Double.valueOf(fontSize).intValue(); newFontData.setHeight(val); } String fontStyle = fontElement.getAttributeValue(ATTRIBUTE_FONTSTYLE); if (hasValue(fontStyle)) { int styleValue = SWT.NORMAL; if (fontStyle.contains("bold")) { styleValue |= SWT.BOLD; } if (fontStyle.contains("italic")) { styleValue |= SWT.ITALIC; } newFontData.setStyle(styleValue); } fontObject.setFont(newFontData.toString()); // Font color fontObject.setFontColor(getRGBColorString(fontElement.getChild(ELEMENT_FONTCOLOR, OPEN_GROUP_NAMESPACE))); } /** * Get the RGB String for an element, or null. */ String getRGBColorString(Element rgbElement) throws XMLModelParserException { String colorStr = null; if (rgbElement != null) { String rString = rgbElement.getAttributeValue(ATTRIBUTE_R); String gString = rgbElement.getAttributeValue(ATTRIBUTE_G); String bString = rgbElement.getAttributeValue(ATTRIBUTE_B); if (!hasValue(rString) || !hasValue(gString) || !hasValue(bString)) { throw new XMLModelParserException("RGB value not found"); } int red = Integer.valueOf(rString); int green = Integer.valueOf(gString); int blue = Integer.valueOf(bString); colorStr = ColorFactory.convertRGBToString(new RGB(red, green, blue)); } return colorStr; } String getChildElementText(Element parentElement, String childElementName, boolean normalise) { //Check for localised element according to the system's locale String code = Locale.getDefault().getLanguage(); if (code == null) { code = "en"; } for (Element childElement : parentElement.getChildren(childElementName, OPEN_GROUP_NAMESPACE)) { String lang = childElement.getAttributeValue(ATTRIBUTE_LANG, Namespace.XML_NAMESPACE); if (code.equals(lang)) { return normalise ? childElement.getTextNormalize() : childElement.getText(); } } // Default to first element found Element element = parentElement.getChild(childElementName, OPEN_GROUP_NAMESPACE); return element == null ? null : normalise ? element.getTextNormalize() : element.getText(); } boolean hasValue(String val) { return StringUtils.isSet(val); } }