| /********************************************************************************* |
| * Copyright (c) 2020, 2021 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| ******************************************************************************** |
| */ |
| |
| package org.eclipse.app4mc.amalthea.converters097.impl; |
| |
| import java.io.File; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.app4mc.amalthea.converters.common.ServiceConstants; |
| import org.eclipse.app4mc.amalthea.converters.common.base.ICache; |
| import org.eclipse.app4mc.amalthea.converters.common.base.IConverter; |
| import org.eclipse.app4mc.amalthea.converters.common.converter.AbstractConverter; |
| import org.eclipse.app4mc.amalthea.converters.common.utils.AmaltheaNamespaceRegistry; |
| import org.eclipse.app4mc.amalthea.converters.common.utils.HelperUtil; |
| import org.eclipse.app4mc.amalthea.converters.common.utils.ModelVersion; |
| import org.eclipse.app4mc.amalthea.converters097.utils.ComponentPortInterfaceCacheBuilder; |
| import org.eclipse.app4mc.util.sessionlog.SessionLogger; |
| import org.jdom2.Attribute; |
| import org.jdom2.Document; |
| import org.jdom2.Element; |
| import org.osgi.service.component.annotations.Activate; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Reference; |
| |
| @Component( |
| property = { |
| ServiceConstants.INPUT_MODEL_VERSION_PROPERTY + "=0.9.6", |
| ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.9.7"}, |
| service = IConverter.class) |
| |
| public class ComponentsModelConverter extends AbstractConverter { |
| |
| @Reference |
| SessionLogger logger; |
| |
| ComponentPortInterfaceCacheBuilder cache; |
| |
| @Override |
| @Activate |
| protected void activate(Map<String, Object> properties) { |
| super.activate(properties); |
| } |
| |
| @Override |
| public void convert(File targetFile, Map<File, Document> fileDocumentMapping, List<ICache> caches) { |
| logger.info("Migration from 0.9.6 to 0.9.7 : Executing ComponentsModel converter for model file : {0}", targetFile.getName()); |
| |
| this.cache = getComponentPortInterfaceCache(caches); |
| if (this.cache == null) { |
| throw new IllegalStateException("ComponentPortInterfaceCacheBuilder is not built and Object of it is not available in Converters"); |
| } |
| |
| basicConvert(targetFile, fileDocumentMapping); |
| } |
| |
| /** |
| * This method is used to get the ComponentPortInterfaceCacheBuilder object |
| * |
| * @param caches The list of all caches. |
| * @return ComponentPortInterfaceCacheBuilder |
| */ |
| private ComponentPortInterfaceCacheBuilder getComponentPortInterfaceCache(List<ICache> caches) { |
| if (caches != null) { |
| for (ICache c : caches) { |
| if (c instanceof ComponentPortInterfaceCacheBuilder) { |
| return (ComponentPortInterfaceCacheBuilder) c; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void basicConvert(File file, Map<File, Document> map) { |
| //get the components with interfaces |
| final Document document = map.get(file); |
| if (document == null) { |
| return; |
| } |
| |
| final Element rootElement = document.getRootElement(); |
| updateComponentTaskRefs(rootElement); |
| updateCallGraphs(rootElement); |
| updatePortInterfaceNames(rootElement,file); |
| updatePortReferences(rootElement); |
| updatePortComponentInstanceReferences(rootElement); |
| } |
| |
| private void updateComponentTaskRefs(Element rootElement) { |
| final String xpathStr = "./componentsModel/components[@tasks]"; |
| |
| final List<Element> components = HelperUtil.getXpathResult( |
| rootElement, |
| xpathStr, |
| Element.class, |
| AmaltheaNamespaceRegistry.getNamespace(getOutputModelVersion(), "am"), |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| if (components == null) { |
| return; |
| } |
| |
| for (Element component : components) { |
| Attribute attribute = component.getAttribute("tasks"); |
| attribute.setName("processes"); |
| } |
| |
| } |
| |
| private void updateCallGraphs(Element rootElement) { |
| final StringBuilder xpathBuilder = new StringBuilder(); |
| xpathBuilder.append("./swModel/tasks/callGraph"); |
| xpathBuilder.append("|"); |
| xpathBuilder.append("./swModel/isrs/callGraph"); |
| xpathBuilder.append("|"); |
| xpathBuilder.append("./swModel/runnables/callGraph"); |
| |
| final List<Element> callGraphs = HelperUtil.getXpathResult( |
| rootElement, |
| xpathBuilder.toString(), |
| Element.class, |
| AmaltheaNamespaceRegistry.getNamespace(getOutputModelVersion(), "am"), |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| for (Element element : callGraphs) { |
| element.setName("activityGraph"); |
| } |
| } |
| |
| private void updatePortInterfaceNames(Element rootElement, File file) { |
| StringBuilder xpathStr = new StringBuilder(); |
| xpathStr.append("./componentsModel/components/ports[@xsi:type=\"am:InterfacePort\"]"); |
| |
| final List<Element> componentPorts = HelperUtil.getXpathResult( |
| rootElement, |
| xpathStr.toString(), |
| Element.class, |
| AmaltheaNamespaceRegistry.getNamespace(ModelVersion._097, "am"), |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| boolean isLocal = cache.getCacheMap().containsKey(file); |
| |
| if (isLocal) { |
| for (String interfaceName : this.cache.getCachedInterfaceNames()) { |
| createInterface(rootElement, interfaceName, file); |
| } |
| } |
| |
| for (Element port : componentPorts) { |
| //update InterfacePort to ComponentPort |
| Attribute typeAttr = port.getAttribute("type", AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| String typeValue = typeAttr.getValue(); |
| typeValue = typeValue.replace("am:InterfacePort", "am:ComponentPort"); |
| typeAttr.setValue(typeValue); |
| |
| //create Interface |
| Attribute interfaceAttr = port.getAttribute("interfaceName"); |
| if (interfaceAttr == null) { |
| continue; |
| } |
| //check if the interface is already created then create amlt ref |
| String value = interfaceAttr.getValue(); |
| |
| if (value == null || value.trim().isEmpty()) { |
| //remove the interfaceName attribute & move to next element in the loop |
| interfaceAttr.detach(); |
| continue; |
| } |
| |
| if (isLocal) { |
| interfaceAttr.setName("interface"); |
| interfaceAttr.setValue(HelperUtil.encodeName(value)+"?type=MainInterface"); |
| } else { |
| Element interfaceEle = new Element("interface"); |
| interfaceEle.setAttribute("type", "am:MainInterface", AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| interfaceEle.setAttribute("href", "amlt:/#" + (HelperUtil.encodeName(value)) + "?type=MainInterface"); |
| port.addContent(interfaceEle); |
| interfaceAttr.detach(); |
| } |
| |
| //remove type interfacePort |
| typeAttr.detach(); |
| } |
| } |
| |
| private void createInterface(Element rootElement, String interfaceName, File file) { |
| String xpath = "./componentsModel"; |
| List<Element> componentsModel = HelperUtil.getXpathResult( |
| rootElement, |
| xpath, |
| Element.class, |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| Element interfaceEle = new Element("interfaces"); |
| interfaceEle.setAttribute("name", HelperUtil.encodeName(interfaceName)); |
| if (!componentsModel.isEmpty()) { |
| componentsModel.get(0).addContent(interfaceEle); |
| } else { |
| Element componentsModelElement = new Element("componentsModel"); |
| componentsModelElement.addContent(interfaceEle); |
| rootElement.addContent(componentsModelElement); |
| } |
| |
| logger.info("Migration from 0.9.6 to 0.9.7 : Interface {0} created in File : {1}", interfaceName, file.getName()); |
| } |
| |
| private void updatePortReferences(Element rootElement) { |
| StringBuilder xPathStr = new StringBuilder(); |
| xPathStr.append("./swModel/tasks/activityGraph/items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/isrs/activityGraph/items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/runnables/activityGraph/items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/tasks/activityGraph/items//items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/isrs/activityGraph/items//items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/runnables/activityGraph/items//items[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/tasks/activityGraph/items//port[@xsi:type=\"am:InterfacePort\"]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/isrs/activityGraph/items//port[@xsi:type=\"am:InterfacePort\"]"); |
| xPathStr.append("|"); |
| xPathStr.append("./swModel/runnables/callGraph/items//port[@xsi:type=\"am:InterfacePort\"]"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/systems/connectors//*[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/systems/connectors//port[@xsi:type=\"am:InterfacePort\"]"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/systems/groundedPorts"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/connectors//*[@port]"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/connectors//port[@xsi:type=\"am:InterfacePort\"]"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/groundedPorts"); |
| |
| final List<Element> ports = HelperUtil.getXpathResult( |
| rootElement, |
| xPathStr.toString(), |
| Element.class, |
| AmaltheaNamespaceRegistry.getNamespace(ModelVersion._097, "am"), |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| for (Element element : ports) { |
| updatePortAttributes(element); |
| } |
| } |
| |
| private void updatePortAttributes(Element element) { |
| Attribute port = element.getAttribute("port"); |
| if (port == null ) { |
| port = element.getAttribute("href"); |
| } |
| |
| String value = port.getValue(); |
| value = value.replace("?type=InterfacePort", "?type=ComponentPort"); |
| port.setValue(value); |
| //remove type for href |
| Attribute type = element.getAttribute("type", AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| if (type != null && type.getValue().equals("am:InterfacePort")) { |
| type.detach(); |
| } |
| } |
| |
| private void updatePortComponentInstanceReferences(Element rootElement) { |
| StringBuilder xPathStr = new StringBuilder(); |
| xPathStr.append("./componentsModel/systems/connectors/sourcePort"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/systems/connectors/targetPort"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/systems/groundedPorts"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/connectors/sourcePort"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/connectors/targetPort"); |
| xPathStr.append("|"); |
| xPathStr.append("./componentsModel/components/groundedPorts"); |
| |
| Map<String, String> parentNames = cache.getCachedComponentInstanceParentNames(); |
| |
| final List<Element> qualifiedPorts = HelperUtil.getXpathResult( |
| rootElement, |
| xPathStr.toString(), |
| Element.class, |
| AmaltheaNamespaceRegistry.getNamespace(ModelVersion._097, "am"), |
| AmaltheaNamespaceRegistry.getGenericNamespace("xsi")); |
| |
| for (Element element : qualifiedPorts) { |
| Attribute instanceAttr = element.getAttribute("instance"); |
| if (instanceAttr == null) { |
| Element hrefInstance = element.getChild("instance"); |
| if (hrefInstance != null) { |
| logger.warn("Qualified Port is having a reference to component instance {0} from another model", |
| hrefInstance.getAttributeValue("href")); |
| } |
| continue; |
| } |
| String value = instanceAttr.getValue(); |
| if (value.matches("(.*)\\?type=ComponentInstance")) { |
| String componentName = value.substring(0, value.indexOf("?type=ComponentInstance")); |
| |
| String encodedParentName = parentNames.get(componentName); |
| if (encodedParentName != null) { |
| instanceAttr.setValue(encodedParentName + "/" + value); |
| } |
| } |
| } |
| |
| } |
| |
| } |