blob: c6082d0a36c4ab789399ecd9ca8326198b7bac77 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2015-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.converters081.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
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.converters081.utils.ConstraintElementsCacheBuilder;
import org.eclipse.app4mc.amalthea.converters081.utils.ConstraintElementsCacheEnum;
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;
/**
* This class is responsible for converting the Constraints Model elements from 0.8.0 to 0.8.1 version format of
* AMALTHEA model
*
* @author mez2rng
*
*/
@Component(
property = {
ServiceConstants.INPUT_MODEL_VERSION_PROPERTY + "=0.8.0",
ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.8.1"},
service = IConverter.class)
public class ConstraintsConverter extends AbstractConverter {
private static final String AMLT_PREFIX = "amlt:/#";
private static final String HREF = "href";
private static final String TYPE = "type";
private static final String XSI = "xsi";
private static final String VALUE = "value";
private static final String SCOPE = "scope";
private static final String EVENT_CHAIN = "eventChain";
@Reference
SessionLogger logger;
private ConstraintElementsCacheBuilder 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.8.0 to 0.8.1 : Executing Constraints converter for model file : {0}",
targetFile.getName());
this.cache = getConstraintElementsCacheBuilder(caches);
if (this.cache == null) {
throw new IllegalStateException("ConstraintElementsCacheBuilder is not built and Object of it is not available in Converters");
}
final Document root = fileDocumentMapping.get(targetFile);
if (root == null) {
return;
}
final Element rootElement = root.getRootElement();
updateEventChainElementDefinitions_and_references(rootElement);
}
/**
* This method is used for the migration of EventChain definitions and references present inside the Constraints
* model (For further details, check : Bug 518119 )
*
* @param rootElement
* Amalthea root element
*/
private void updateEventChainElementDefinitions_and_references(final Element rootElement) {
final List<Element> rootEventChainElements = HelperUtil.getXpathResult(
rootElement,
"./constraintsModel/eventChains",
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
final List<String> allRootEventChainElements = getAllRootEventChainElements();
if (! rootEventChainElements.isEmpty()) {
for (final Element rootEventChainElement : rootEventChainElements) {
final List<Element> subEventChainElements = HelperUtil.getXpathResult(
rootEventChainElement,
".//*[@xsi:type=\"am:SubEventChain\"]",
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
for (final Element subEventChainElement : subEventChainElements) {
final Attribute typeAttribute = subEventChainElement.getAttribute(TYPE,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
if (typeAttribute != null) {
typeAttribute.setValue("am:EventChainContainer");
}
}
updateEventChainReferences(allRootEventChainElements, rootEventChainElement);
}
}
updateEventChainReferences_in_TimingConstraints(allRootEventChainElements, rootElement);
}
/**
* This method is used to migrate the contents of TimingConstraint elements of type EventChainLatencyConstraint and
* EventChainSynchronizationConstraint, which are present inside Constraints Model.
*
* <b>Note:</b> As a part of migration, it should be ensured that sub EventChain elements are not directly referred
* inside TimingConstraint elements. Till 0.8.1, it was possible to refer sub EventChain elements --> but based on
* this change such references are removed. To preserve the data specified by the user, reference String is stored
* as a CustomProperty
*
*
* @param allRootEventChainElements
* List<String> contains the names of all the root EventChain elements (in the entire model scope)
* @param rootElement
* Amalthea root element
*
*/
private void updateEventChainReferences_in_TimingConstraints(final List<String> allRootEventChainElements, final Element rootElement) {
final List<Element> eventChainReferenceEleemnts = HelperUtil.getXpathResult(
rootElement,
"./constraintsModel/timingConstraints[@xsi:type=\"am:EventChainLatencyConstraint\" or @xsi:type=\"am:EventChainSynchronizationConstraint\"]",
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
for (final Element timingConstraint : eventChainReferenceEleemnts) {
final List<String> invalidEventChainRefs = new ArrayList<>();
final Attribute eventChainsAttribute = timingConstraint.getAttribute(SCOPE);
if (eventChainsAttribute != null) {
boolean isRefEventChainStringsUpdated = false;
String refEventChainStrings = eventChainsAttribute.getValue();
final StringTokenizer stringTokenizer = new StringTokenizer(refEventChainStrings);
while (stringTokenizer.hasMoreTokens()) {
final String refEventChainString = stringTokenizer.nextToken();
final String refEventChainName = refEventChainString.substring(0, refEventChainString.lastIndexOf('?'));
/*- performing validation, to identify if childelements are referred */
if (!allRootEventChainElements.contains(refEventChainName)) {
isRefEventChainStringsUpdated = true;
/*- this is the case child EventChain element is referred (Note: As per AMALTHEA 0.8.1, it is not supported)*/
refEventChainStrings = refEventChainStrings.replace(refEventChainString, "");
/*- adding invalid EventChain reference string to a list -> to create CustomProperty object later */
invalidEventChainRefs.add(refEventChainString);
if (refEventChainStrings.trim().length() == 0) {
timingConstraint.removeAttribute(eventChainsAttribute);
}
logEventChainMessage_TimingConstraint(timingConstraint, refEventChainName);
}
}
if (isRefEventChainStringsUpdated) {
/*- setting the updated EventChain reference String */
eventChainsAttribute.setValue(refEventChainStrings.trim());
}
}
/*-
* If EventChain element is defined in other model file, then the reference inside TimingConstraint element will be generated as a separate tag in XMI
*
* Example:
*
* <timingConstraints xsi:type="am:EventChainLatencyConstraint" name="ecl2">
* <scope href="amlt:/#eventChain_from_second_file_sub_element?type=EventChain"/>
* </timingConstraints>
* */
final List<Element> eventChains = timingConstraint.getChildren(SCOPE);
if (eventChains != null) {
for (final Element eventChainElement : eventChains) {
final String hrefValue = eventChainElement.getAttributeValue(HREF);
if (hrefValue != null && hrefValue.contains(AMLT_PREFIX)) {
final String refEventChainName = hrefValue.substring(hrefValue.indexOf('#') + 1, hrefValue.indexOf('?'));
/*- performing validation, to identify if childelements are referred */
if (!allRootEventChainElements.contains(refEventChainName)) {
/*- this is the case child EventChain element is referred (Note: As per AMALTHEA 0.8.1, it is not supported)*/
timingConstraint.removeContent(eventChainElement);
invalidEventChainRefs.add(hrefValue);
logEventChainMessage_TimingConstraint(timingConstraint, refEventChainName);
}
}
}
}
/*-
* =================== Creating CustomProperty and associating invalid EventChain reference Strings to it. This step is performed to avoid loss of data ===========
*/
if (! invalidEventChainRefs.isEmpty()) {
final Element customPropertyElement = new Element("customProperties");
customPropertyElement.setAttribute("key", SCOPE);
final Element valueElement = new Element(VALUE);
valueElement.setAttribute(TYPE, "am:ListObject", AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
for (final String invalidEventChainRef : invalidEventChainRefs) {
final Element valuesElement = new Element("values");
valuesElement.setAttribute(TYPE, "am:StringObject", AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
valuesElement.setAttribute(VALUE, invalidEventChainRef);
valueElement.addContent(valuesElement);
}
customPropertyElement.addContent(valueElement);
timingConstraint.addContent(customPropertyElement);
}
}
}
/**
* This method is used to migrate the contents of EventChainReference objects which are present inside EventChain
* object in Constraints Model.
*
* <b>Note:</b> As a part of migration, it should be ensured that sub EventChain elements are not directly referred
* inside EventChainReference elements. Till 0.8.1, there was a possibility to refer sub EventChain elements --> but
* based on this change such references are removed. To preserve the data specified by the user, reference String is
* stored as a CustomProperty
*
*
* @param allRootEventChainElements
* List<String> contains the names of all the root EventChain elements (in the entire model scope)
* @param rootEventChainElement
* EventChain element which is directly present inside Constraints Model
*/
private void updateEventChainReferences(final List<String> allRootEventChainElements, final Element rootEventChainElement) {
final List<Element> eventChainReferenceEleemnts = HelperUtil.getXpathResult(
rootEventChainElement,
".//*[@xsi:type=\"am:EventChainReference\"]",
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
for (final Element eventChainReferenceElement : eventChainReferenceEleemnts) {
final Attribute eventChainReferenceAttribute = eventChainReferenceElement.getAttribute(EVENT_CHAIN);
if (eventChainReferenceAttribute != null) {
final String refEventChainString = eventChainReferenceAttribute.getValue();
if (refEventChainString != null && refEventChainString.contains("?")) {
final String refEventChainName = refEventChainString.substring(0,
refEventChainString.lastIndexOf('?'));
/*- performing validation, to identify if childelements are referred */
/*
* ASSUMPTION !!! : In case of model element reference, elements name should be already encoded as a part of the reference.
*
* allRootEventChainElements -> obtained from the CacheBuilder should already contain encoded names of the EventChain elements
*/
if (!allRootEventChainElements.contains((refEventChainName))) {
/*- this is the case child EventChain element is referred (Note: As per AMALTHEA 0.8.1, it is not supported)*/
eventChainReferenceElement.removeAttribute(eventChainReferenceAttribute);
logEventChainMessage_and_Create_CustomProperty(rootEventChainElement,
eventChainReferenceElement, refEventChainString, refEventChainName);
}
}
}
/*-
* if EventChain element is defined in other model file, then the eventChain element will be generated as a separate tag in XMI
*
* Example:
*
* <segments xsi:type="am:EventChainReference">
* <eventChain href="amlt:/#eventChain_from_second_file?type=EventChain"/>
* </segments>
*
* */
final Element eventChainElement = eventChainReferenceElement.getChild(EVENT_CHAIN);
if (eventChainElement != null) {
final String hrefValue = eventChainElement.getAttributeValue(HREF);
if (hrefValue != null && hrefValue.contains(AMLT_PREFIX)) {
final String refEventChainName = hrefValue.substring(hrefValue.indexOf('#') + 1,
hrefValue.indexOf('?'));
/*- performing validation, to identify if childelements are referred */
/*
* ASSUMPTION !!! : In case of model element reference (refEventChainName), elements name should be already encoded as a part of the reference.
*
* allRootEventChainElements -> obtained from the CacheBuilder should already contain encoded names of the EventChain elements
*/
if (!allRootEventChainElements.contains((refEventChainName))) {
/*- this is the case child EventChain element is referred (Note: As per AMALTHEA 0.8.1, it is not supported)*/
eventChainReferenceElement.removeContent(eventChainElement);
logEventChainMessage_and_Create_CustomProperty(rootEventChainElement,
eventChainReferenceElement, hrefValue, refEventChainName);
}
}
}
}
}
private void logEventChainMessage_TimingConstraint(final Element timingConstraintElement,
final String refEventChainName) {
logger.warn("Sub EventChain : {0} is referred inside TimingConstraint as \"Scope\": {1}. "
+ "As per 0.8.1, it is not valid to refer sub EventChain elements directly.\n\r"
+ "EventChain {2}'s association is removed from TimingConstraint object, and corresponding information is stored as a CustomProperty",
HelperUtil.decodeName(refEventChainName),
timingConstraintElement.getAttributeValue("name"),
refEventChainName);
}
private void logEventChainMessage_and_Create_CustomProperty(final Element rootEventChainElement,
final Element eventChainReferenceElement, final String refEventChainString,
final String refEventChainName) {
logger.warn("Sub EventChain : {0} is referred inside EventChainReference of EventChain : {1}."
+ " As per 0.8.1, it is not valid to refer sub EventChain elements inside EventChainReference.\n\r"
+ " eventChain association is removed from EventChainReference object, and corresponding information is stored as a CustomProperty",
HelperUtil.decodeName(refEventChainName),
rootEventChainElement.getAttributeValue("name"));
final Element customPropertyElement = new Element("customProperties");
customPropertyElement.setAttribute("key", EVENT_CHAIN);
final Element valueElement = new Element(VALUE);
valueElement.setAttribute(TYPE, "am:StringObject", AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
valueElement.setAttribute(VALUE, refEventChainString);
customPropertyElement.addContent(valueElement);
eventChainReferenceElement.addContent(customPropertyElement);
}
/**
* This method returns the names of all root EventChain elements -- from different AMALTHEA models which are in
* folder scope
*
* @return
*/
@SuppressWarnings("unchecked")
private List<String> getAllRootEventChainElements() {
ArrayList<String> rootEventChainNames = new ArrayList<>();
Map<File, Map<String, Object>> cacheMap = this.cache.getCacheMap();
Collection<Map<String, Object>> values = cacheMap.values();
for (Map<String, Object> map : values) {
Object value = map.get(ConstraintElementsCacheEnum.ROOT_EVENTCHAIN_NAMES.name());
if (value instanceof List<?>) {
rootEventChainNames.addAll((Collection<? extends String>) value);
}
}
return rootEventChainNames;
}
/**
* This method is used to get the ConstraintElementsCacheBuilder object
*
* @param caches The list of all caches.
* @return ConstraintElementsCacheBuilder
*/
private ConstraintElementsCacheBuilder getConstraintElementsCacheBuilder(List<ICache> caches) {
if (caches != null) {
for (ICache c : caches) {
if (c instanceof ConstraintElementsCacheBuilder) {
return (ConstraintElementsCacheBuilder) c;
}
}
}
return null;
}
}