/** | |
******************************************************************************** | |
* Copyright (c) 2019, 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.converters094.impl; | |
import java.io.File; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.regex.Matcher; | |
import java.util.regex.Pattern; | |
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.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.3", | |
ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.9.4"}, | |
service = IConverter.class) | |
public class SwConverter extends AbstractConverter { | |
private static final String AM = "am"; | |
private static final String XSI = "xsi"; | |
private static final String HREF = "href"; | |
private static final String NAME = "name"; | |
private static final String TYPE = "type"; | |
private static final String VALUE = "value"; | |
private static final String AMLT_PREFIX = "amlt:/#"; | |
@Reference | |
SessionLogger logger; | |
@Override | |
@Activate | |
protected void activate(Map<String, Object> properties) { | |
super.activate(properties); | |
} | |
@Override | |
public void convert(File targetFile, Map<File, Document> filename2documentMap, List<ICache> caches) { | |
logger.info("Migration from 0.9.3 to 0.9.4 : Executing Sw converter for model file : {0}", targetFile.getName()); | |
final Document document = filename2documentMap.get(targetFile); | |
if (document == null) { | |
return; | |
} | |
final Element rootElement = document.getRootElement(); | |
updateRunnables(rootElement); | |
updateDatatypes(rootElement); | |
updateModeLabels(rootElement); | |
updateModeLabelAccesses(rootElement); | |
updateModeValueLists(rootElement); | |
updateModeConditions(rootElement); | |
updateEnablingModeValueLists(rootElement); | |
} | |
private void updateDatatypes(final Element rootElement) { | |
final String xpath ="./swModel/typeDefinitions[@xsi:type=\"am:BaseTypeDefinition\"]"; | |
final List<Element> types = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element baseType : types) { | |
for (Element mapping : baseType.getChildren("dataMapping")) { | |
String platformName = mapping.getAttributeValue("platformName"); | |
String platformType = mapping.getAttributeValue("platformType"); | |
mapping.removeAttribute("platformName"); | |
mapping.removeAttribute("platformType"); | |
mapping.setName("aliases"); | |
mapping.setAttribute("target", platformName); | |
mapping.setAttribute("alias", platformType); | |
} | |
} | |
} | |
private void updateRunnables(final Element rootElement) { | |
final String xpath = "./swModel/runnables"; | |
final String deadlineXpath = "./swModel/runnables/deadline"; | |
final List<Element> runnables = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
final List<Element> deadlines = HelperUtil.getXpathResult( | |
rootElement, | |
deadlineXpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
Element constraintModel = rootElement.getChild("constraintsModel"); | |
if (constraintModel == null && !deadlines.isEmpty()) { | |
constraintModel = new Element("constraintsModel"); | |
rootElement.addContent(constraintModel); | |
} | |
if (constraintModel != null) { | |
for (Element runnable : runnables) { | |
Element deadline = runnable.getChild("deadline"); | |
String runnableName = runnable.getAttributeValue(NAME); | |
if (deadline != null) { | |
// create RunnableRequirement | |
Element runnableReq = new Element("requirements"); | |
constraintModel.addContent(runnableReq); | |
runnableReq.setAttribute(TYPE, "am:RunnableRequirement", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
runnableReq.setAttribute(NAME, "deadline"); | |
runnableReq.setAttribute("runnable", HelperUtil.encodeName(runnableName) + "?type=Runnable"); | |
Element limit = new Element("limit"); | |
runnableReq.addContent(limit); | |
limit.setAttribute(TYPE, "am:TimeRequirementLimit", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
limit.setAttribute("limitType", "UpperLimit"); | |
limit.setAttribute("metric", "ResponseTime"); | |
Element limitValue = deadline.clone(); | |
limitValue.setName("limitValue"); | |
limit.addContent(limitValue); | |
runnable.removeContent(deadline); | |
} | |
} | |
} | |
} | |
private void updateModeLabels(final Element rootElement) { | |
String xpath; | |
// convert Modes | |
xpath = "./swModel/modes"; | |
final List<Element> modes = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element mode : modes) { | |
mode.setAttribute(TYPE, "am:EnumMode", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
} | |
// convert ModeLabels | |
xpath = "./swModel/modeLabels"; | |
final List<Element> modeLabels = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element modeLabel : modeLabels) { | |
// modify attribute (standard reference) | |
String attributeValue = modeLabel.getAttributeValue("initialValue"); | |
if (attributeValue != null) { | |
// pattern: "mode/literal?type=modeliteral" | |
Pattern p = Pattern.compile("(.+)/(.+)\\?type=ModeLiteral"); | |
Matcher m = p.matcher(attributeValue); | |
if (m.find()) { | |
String mode = m.group(1); | |
String literal = HelperUtil.decodeName(m.group(2)); | |
modeLabel.setAttribute("mode", mode + "?type=EnumMode"); | |
modeLabel.setAttribute("initialValue", literal); | |
} | |
} | |
// modify content (cross file reference) | |
Element element = getSingleChild(modeLabel, "initialValue"); | |
if (element != null) { | |
String hrefValue = element.getAttributeValue(HREF); | |
if (hrefValue != null) { | |
// pattern: "amlt:/#mode/literal?type=modeliteral" | |
Pattern p = Pattern.compile("amlt:/#(.+)/(.+)\\?type=ModeLiteral"); | |
Matcher m = p.matcher(hrefValue); | |
if (m.find()) { | |
String mode = m.group(1); | |
String literal = HelperUtil.decodeName(m.group(2)); | |
element.setName("mode"); | |
element.setAttribute(TYPE, "am:EnumMode", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
element.setAttribute(HREF, AMLT_PREFIX + mode + "?type=EnumMode"); | |
modeLabel.setAttribute("initialValue", literal); | |
} | |
} | |
} | |
} | |
} | |
private void updateModeLabelAccesses(final Element rootElement) { | |
// convert mode label access in RunnableItems | |
final String xpath = "./swModel/runnables//*[@xsi:type=\"am:ModeLabelAccess\"]"; | |
final List<Element> modeLabelAccesses = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element modeLabelAccess : modeLabelAccesses) { | |
// convert access "write" to "set" | |
Attribute access = modeLabelAccess.getAttribute("access"); | |
if (access != null && "write".equals(access.getValue())) { | |
access.setValue("set"); | |
} | |
// modify attribute (standard reference) | |
Attribute modeValue = modeLabelAccess.getAttribute("modeValue"); | |
if (modeValue != null) { | |
modeValue.setName(VALUE); | |
modeValue.setValue(extractAndDecodeLiteral(modeValue.getValue())); | |
} | |
// modify content (cross file reference) | |
Element element = getSingleChild(modeLabelAccess, "modeValue"); | |
if (element != null) { | |
String hrefValue = element.getAttributeValue(HREF); | |
if (hrefValue != null) { | |
modeLabelAccess.setAttribute(VALUE, extractAndDecodeLiteral(hrefValue)); | |
modeLabelAccess.removeChild("modeValue"); | |
} | |
} | |
} | |
} | |
private void updateModeValueLists(final Element rootElement) { | |
// convert mode assignments in mode value lists | |
final String xpath ="./stimuliModel/stimuli/setModeValueList/entries"; | |
final List<Element> assignments = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element assignment : assignments) { | |
convertModeValueContents(assignment); | |
} | |
} | |
private void updateModeConditions(final Element rootElement) { | |
// handle mode value | |
final String xpath = | |
"./swModel/runnables//*[@xsi:type=\"am:RunnableModeSwitch\"]/entries/condition/entries" | |
+ "|./swModel/tasks//*[@xsi:type=\"am:ModeSwitch\"]/entries/condition/entries" | |
+ "|./stimuliModel/stimuli/enablingModeValueList/entries"; | |
final List<Element> conditionDisjunctionEntries = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element disjunctionEntry : conditionDisjunctionEntries) { | |
String entryType = disjunctionEntry.getAttributeValue(TYPE, AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
if (entryType.equals("am:ModeValue")) { | |
convertModeValue(disjunctionEntry, true); | |
} | |
else if (entryType.equals("am:ModeValueConjunction")) { | |
disjunctionEntry.setAttribute(TYPE, "am:ModeConditionConjunction", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element modeValue : disjunctionEntry.getChildren("entries")) { | |
convertModeValue(modeValue, false); | |
} | |
} | |
} | |
} | |
private void convertModeValue(final Element modeValueElement, boolean setType) { | |
if (setType) { | |
modeValueElement.setAttribute(TYPE, "am:ModeCondition", AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
} | |
modeValueElement.setAttribute("relation", "EQUAL"); | |
convertModeValueContents(modeValueElement); | |
} | |
private void convertModeValueContents(final Element modeValueElement) { | |
// 1. rename valueProvider to label | |
// - modify attribute (standard reference) | |
Attribute providerAttribute = modeValueElement.getAttribute("valueProvider"); | |
if (providerAttribute != null) { | |
providerAttribute.setName("label"); | |
} | |
// - modify content (cross file reference) | |
Element providerElement = getSingleChild(modeValueElement, "valueProvider"); | |
if (providerElement != null) { | |
providerElement.setName("label"); | |
} | |
// 2. extract and set value | |
// modify attribute (standard reference) | |
String refValue = modeValueElement.getAttributeValue(VALUE); | |
if (refValue != null) { | |
modeValueElement.setAttribute(VALUE, extractAndDecodeLiteral(refValue)); | |
} | |
// modify content (cross file reference) | |
Element valueElement = getSingleChild(modeValueElement, VALUE); | |
if (valueElement != null) { | |
String hrefValue = valueElement.getAttributeValue(HREF); | |
if (hrefValue != null) { | |
modeValueElement.setAttribute(VALUE, extractAndDecodeLiteral(hrefValue)); | |
modeValueElement.removeChild(VALUE); | |
} | |
} | |
} | |
private void updateEnablingModeValueLists(final Element rootElement) { | |
final String xpath = "./stimuliModel/stimuli"; | |
final List<Element> stimuli = HelperUtil.getXpathResult( | |
rootElement, | |
xpath, | |
Element.class, | |
AmaltheaNamespaceRegistry.getNamespace(ModelVersion._094, AM), | |
AmaltheaNamespaceRegistry.getGenericNamespace(XSI)); | |
for (Element stimulus : stimuli) { | |
final String stimulusName = stimulus.getAttributeValue(NAME); | |
// rename enabling list | |
Element enablingList = getSingleChild(stimulus, "enablingModeValueList"); | |
if(enablingList != null) { | |
enablingList.setName("executionCondition"); | |
} | |
// remove disabling list | |
Element disablingList = getSingleChild(stimulus, "disablingModeValueList"); | |
if (disablingList != null) { | |
logger.warn("From Stimulus : {0}, disablingModeValueList element is removed (as this element is no longer supported by AMALTHEA meta model from 0.9.4 version) ", stimulusName); | |
stimulus.removeChild("disablingModeValueList"); | |
} | |
} | |
} | |
// helper methods | |
private String extractAndDecodeLiteral(String modeValueReference) { | |
String value = modeValueReference; | |
Pattern p = Pattern.compile(".*/(.+?)\\?type=ModeLiteral"); | |
Matcher m = p.matcher(value); | |
if(m.find()) { | |
return HelperUtil.decodeName(m.group(1)); | |
} | |
return modeValueReference; | |
} | |
private Element getSingleChild(final Element parent, String name) { | |
List<Element> list = parent.getChildren(name); | |
if (list.size() == 1) { | |
return list.get(0); | |
} else { | |
return null; | |
} | |
} | |
} |