blob: bb35c22791f6dd0125e0c67431433b7de1a14311 [file] [log] [blame]
/**
********************************************************************************
* 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;
}
}
}