blob: 7551cc362b0fca7efb1e69c798ffafb1ba31cd02 [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.converters072.impl;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
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.converters072.utils.HwElementsCacheBuilder;
import org.eclipse.app4mc.amalthea.converters072.utils.HwElementsCacheEnum;
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.7.1",
ServiceConstants.OUTPUT_MODEL_VERSION_PROPERTY + "=0.7.2"},
service = IConverter.class)
public class PropertyConstraintsConverter extends AbstractConverter {
private static final String AMLT_PREFIX = "amlt:/#";
private static final String XSI = "xsi";
private static final String NAME = "name";
private static final String SYSTEM = "system";
@Reference
SessionLogger logger;
private HwElementsCacheBuilder cache;
private Map<File, Document> fileDocumentMapping;
private File targetFile;
@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.7.1 to 0.7.2 : Executing PropertyConstraints converter for model file : {0}",
targetFile.getName());
this.cache = getHwElementsCacheBuilder(caches);
if (this.cache == null) {
throw new IllegalStateException("HwElementsCacheBuilder is not built and Object of it is not available in Converters");
}
this.fileDocumentMapping = fileDocumentMapping;
this.targetFile = targetFile;
final Document root = fileDocumentMapping.get(targetFile);
if (root == null) return;
final Element rootElement = root.getRootElement();
removeCoreTypesAndMemoryTypes(rootElement);
updateCoreRefs(rootElement);
updateMemoryRefs(rootElement);
}
/**
* Bug 507885:
*
* Based on the change in MetaModel -> CoreType & MemoryType objects should not be defined inside
* PropertyConstraints model.
*
*
* Based on the above point -> EStructuralFeatures inside PropertyConstraintsModel class : coreTypeDefinitions,
* memoryTypeDefinitions are removed as a part of migration.
*
* Definitions of corresponding CoreType and MemoryType are checked if they already exists in HW Models (in model
* scope). If they do not exists, definitions from PropertyConstraints model are moved to HW Model
*
* @param rootElement
* org.jdom2.Element of Amalthea object
*/
@SuppressWarnings("unchecked")
private void removeCoreTypesAndMemoryTypes(final Element rootElement) {
final StringBuilder xpathBuffer = new StringBuilder();
xpathBuffer.append("./propertyConstraintsModel");
final List<Element> propertyConstraintsModelElements = HelperUtil.getXpathResult(
rootElement,
xpathBuffer.toString(),
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
if (!propertyConstraintsModelElements.isEmpty()) {
final Element propertyConstraintsModel = propertyConstraintsModelElements.get(0);
final List<Element> coreTypeDefinitions = propertyConstraintsModel.getChildren("coreTypeDefinitions");
final Iterator<Element> coreTypesIterator = coreTypeDefinitions.iterator();
while (coreTypesIterator.hasNext()) {
final Element coreTypeDefinition = coreTypesIterator.next();
final String coreTypeName = coreTypeDefinition.getAttributeValue(NAME);
/*-removing the element from iterator -> as in below code iterating list is modified*/
coreTypesIterator.remove();
/*- removing the CoreType objects from the PropertyConstraints model */
propertyConstraintsModel.removeContent(coreTypeDefinition);
if (coreTypeName != null) {
/*- Below code is used to check if the CoreType element definition is available in the HW Model*/
if (!isHWElementPresentInCache(HwElementsCacheEnum.CORE_TYPE_NAMES, coreTypeName)) {
Entry<File, Element> entry = getFirstOccuranceOfHwModelFromModelFiles();
if (entry != null) {
File fileAssociatedToHwElement = entry.getKey();
Element hwModelElement = entry.getValue();
/*- renaming the xml tag name from coreTypeDefinitions to coreTypes -> as it will associated to HW Model */
coreTypeDefinition.setName("coreTypes");
/*- associating coreTypes to HW Model */
hwModelElement.addContent(coreTypeDefinition);
/*-newly added coreType elements are updated in the cache */
List<String> coreTypeNames = (List<String>) this.cache.getCacheMap()
.get(fileAssociatedToHwElement).get(HwElementsCacheEnum.CORE_TYPE_NAMES.name());
coreTypeNames.add(coreTypeName);
}
}
}
}
final List<Element> memoryTypeDefinitions = propertyConstraintsModel.getChildren("memoryTypeDefinitions");
final Iterator<Element> memoryTypesIterator = memoryTypeDefinitions.iterator();
while (memoryTypesIterator.hasNext()) {
final Element memoryTypeDefinition = memoryTypesIterator.next();
final String memoryTypeName = memoryTypeDefinition.getAttributeValue(NAME);
/*-removing the element from iterator -> as in below code iterating list is modified*/
memoryTypesIterator.remove();
/*- removing the MemoryType objects from the PropertyConstraints model */
propertyConstraintsModel.removeContent(memoryTypeDefinition);
if (memoryTypeName != null) {
/*- Below code is used to check if the MemoryType element definition is available in the HW Model*/
if (!isHWElementPresentInCache(HwElementsCacheEnum.MEMORY_TYPE_NAMES, memoryTypeName)) {
Entry<File, Element> entry = getFirstOccuranceOfHwModelFromModelFiles();
if (entry != null) {
File fileAssociatedToHwElement = entry.getKey();
Element hwModelElement = entry.getValue();
/*- renaming the xml tag name from memoryTypeDefinitions to memoryTypes -> as it will associated to HW Model */
memoryTypeDefinition.setName("memoryTypes");
/*- associating coreTypes to HW Model */
hwModelElement.addContent(memoryTypeDefinition);
/*-newly added memorType elements are updated in the cache */
List<String> memoryTypeNames = (List<String>) this.cache.getCacheMap()
.get(fileAssociatedToHwElement).get(HwElementsCacheEnum.MEMORY_TYPE_NAMES.name());
memoryTypeNames.add(memoryTypeName);
}
}
}
}
}
}
/**
* Bug 507885: As HW elements should not be created inside PropertyConstraints model -> Model change is made to
* refer the Memory elements from HW Model. <br>
*
* This method will remove the definitions of Memory objects inside PropertyConstraints model and add the memory
* references (defined in HW Model)
*
* - In case if HW Model doesn't exists in any of the AMALTHEA models (in scope) -> then a new HW Model and the
* corresponding Memory objects are created accordingly.
*
* Note: For the newly created Memory elements -> name of the Memory element is set same as the Memory element which
* was defined inside PropertyConstraints model
*
* @param rootElement
* org.jdom2.Element of Amalthea object
*/
@SuppressWarnings("unchecked")
private void updateMemoryRefs(final Element rootElement) {
final StringBuilder xpathBuffer = new StringBuilder();
xpathBuffer.append("./propertyConstraintsModel//firstConstraint[@xsi:type=\"am:HwMemoryProperty\"]/memory");
xpathBuffer.append("|");
xpathBuffer.append("./propertyConstraintsModel//secondConstraint[@xsi:type=\"am:HwMemoryProperty\"]/memory");
xpathBuffer.append("|");
xpathBuffer.append("./propertyConstraintsModel//hwConstraint[@xsi:type=\"am:HwMemoryProperty\"]/memory");
final List<Element> memoryElements = HelperUtil.getXpathResult(
rootElement,
xpathBuffer.toString(),
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
final Iterator<Element> memoryElementsIterator = memoryElements.iterator();
while (memoryElementsIterator.hasNext()) {
final Element memoryElement = memoryElementsIterator.next();
final String memoryName = memoryElement.getAttributeValue(NAME);
final Element parent = memoryElement.getParentElement();
/*- removing the Memory object from PropertyConstraints model */
parent.removeContent(memoryElement);
if (memoryName != null) {
/*- Below code is used to check if the referred Memory element definition is available in the HW Model. If not create the required structure and define Memory objects*/
if (!isHWElementPresentInCache(HwElementsCacheEnum.MEMORY_NAMES, memoryName)) {
Entry<File, Element> entry = getFirstOccuranceOfHwModelFromModelFiles();
if (entry != null) {
File fileAssociatedToHwElement = entry.getKey();
Element hwModelElement = entry.getValue();
Element system = hwModelElement.getChild(SYSTEM);
if (system == null) {
system = new Element(SYSTEM);
system.setAttribute(NAME, "migration_gen_system");
hwModelElement.addContent(system);
}
final Element genMemoryElement = new Element("memories");
genMemoryElement.setAttribute(NAME, memoryName);
system.addContent(genMemoryElement);
/*-newly added memory elements are updated in the cache */
final List<String> memoryNames = (List<String>) this.cache.getCacheMap()
.get(fileAssociatedToHwElement).get(HwElementsCacheEnum.MEMORY_NAMES.name());
memoryNames.add(memoryName);
}
}
/*- Adding refernce to Memory */
if (isElementDefinedInFile(memoryName, HwElementsCacheEnum.MEMORY_NAMES, this.targetFile)) {
parent.setAttribute(new Attribute("memory", encodeName(memoryName) + "?type=Memory"));
}
else {
final Element refMemoryElement = new Element("memory");
refMemoryElement.setAttribute("href", AMLT_PREFIX + encodeName(memoryName) + "?type=Memory");
/*- adding reference to Memory object (which is defined in the HW Model) */
parent.addContent(refMemoryElement);
}
}
}
}
/**
* Bug 507885: As HW elements should not be created inside PropertyConstraints model -> Model change is made to
* refer the Core elements from HW Model. <br>
*
* This method will remove the definitions of Core objects inside PropertyConstraints model and add the references
* to the Core elements (defined in HW Model)
*
* - In case if HW Model doesn't exists in any of the AMALTHEA models (in scope) -> then a new HW Model and the
* corresponding Core objects are created accordingly.
*
* Note: For the newly created Core elements -> name of the Core element is set same as the Core element which was
* defined inside PropertyConstraints model
*
* @param rootElement
* org.jdom2.Element of Amalthea object
*/
@SuppressWarnings("unchecked")
private void updateCoreRefs(final Element rootElement) {
final StringBuilder xpathBuffer = new StringBuilder();
xpathBuffer.append("./propertyConstraintsModel//firstConstraint[@xsi:type=\"am:HwCoreProperty\"]/core");
xpathBuffer.append("|");
xpathBuffer.append("./propertyConstraintsModel//secondConstraint[@xsi:type=\"am:HwCoreProperty\"]/core");
xpathBuffer.append("|");
xpathBuffer.append("./propertyConstraintsModel//hwConstraint[@xsi:type=\"am:HwCoreProperty\"]/core");
final List<Element> coreElements = HelperUtil.getXpathResult(
rootElement,
xpathBuffer.toString(),
Element.class,
AmaltheaNamespaceRegistry.getGenericNamespace(XSI));
final Iterator<Element> coreElementsIterator = coreElements.iterator();
while (coreElementsIterator.hasNext()) {
final Element coreElement = coreElementsIterator.next();
final String coreName = coreElement.getAttributeValue(NAME);
final Element parent = coreElement.getParentElement();
parent.removeContent(coreElement);
if (coreName != null) {
/*- Below code is used to check if the referred Core element definition is available in the HW Model. If not create the required structure and define Core objects*/
if (!isHWElementPresentInCache(HwElementsCacheEnum.CORE_NAMES, coreName)) {
Entry<File, Element> entry = getFirstOccuranceOfHwModelFromModelFiles();
if (entry != null) {
File fileAssociatedToHwElement = entry.getKey();
Element hwModelElement = entry.getValue();
Element system = hwModelElement.getChild(SYSTEM);
if (system == null) {
system = new Element(SYSTEM);
system.setAttribute(NAME, "migration_gen_system");
hwModelElement.addContent(system);
}
final List<Element> ecuElements = HelperUtil.getXpathResult(system,
".//ecus[@name=\"migration_gen_ecu\"]", Element.class);
if (!ecuElements.isEmpty()) {
final Element ecus = ecuElements.get(0);
final List<Element> microControllersList = ecus.getChildren("microcontrollers");
for (final Element microControllers : microControllersList) {
final String microControllerName = microControllers.getAttributeValue(NAME);
if (microControllerName != null
&& microControllerName.equals("migration_gen_microcontroller")) {
/*- associating PC model Core Element to HW Model */
coreElement.setName("cores");
microControllers.addContent(coreElement);
break;
}
}
}
else {
final Element ecus = new Element("ecus");
ecus.setAttribute(NAME, "migration_gen_ecu");
system.addContent(ecus);
final Element microControllers = new Element("microcontrollers");
microControllers.setAttribute(NAME, "migration_gen_microcontroller");
ecus.addContent(microControllers);
/*- associating PC model Core Element to HW Model */
coreElement.setName("cores");
microControllers.addContent(coreElement);
}
/*-newly added core elements are updated in the cache */
final List<String> coreNames = (List<String>) this.cache.getCacheMap()
.get(fileAssociatedToHwElement).get(HwElementsCacheEnum.CORE_NAMES.name());
coreNames.add(coreName);
}
}
/*- Adding reference to Core object*/
if (isElementDefinedInFile(coreName, HwElementsCacheEnum.CORE_NAMES, this.targetFile)) {
parent.setAttribute(new Attribute("core", encodeName(coreName) + "?type=Core"));
}
else {
final Element refCoreElement = new Element("core");
refCoreElement.setAttribute("href", AMLT_PREFIX + encodeName(coreName) + "?type=Core");
/*- adding reference to Core object (which is defined in the HW Model) */
parent.addContent(refCoreElement);
}
}
}
}
private Entry<File, Element> firstOccuranceOfHwModelInModelScope;
/**
* This method is used to get the first HW Model from the model scope (i.e. Model scope can have multiple model
* files with HW Models -> this method is used to return the first HW Model )
*
* @return Entry<File, Element> Entry object -> <br>
* key is the File containing HW Model, <br>
* value is the org.jdom2.Element of "HW Model"
*/
private Entry<File, Element> getFirstOccuranceOfHwModelFromModelFiles() {
if (this.firstOccuranceOfHwModelInModelScope != null) {
return this.firstOccuranceOfHwModelInModelScope;
}
final Map<File, Map<String, Object>> cacheMap = this.cache.getCacheMap();
final Set<File> fileSet = this.fileDocumentMapping.keySet();
final TreeSet<File> sortedInputFileSet = new TreeSet<>(fileSet);
for (final File file : sortedInputFileSet) {
final Map<String, Object> map = cacheMap.get(file);
final Object object = map.get(HwElementsCacheEnum.HwModel.name());
/*-this is the case where HW Model is existing inside the AMALTHEA model file*/
if (object instanceof Element) {
/*- returning Entry object with key as file (containing HW Model), value as the org.jdom2.Element representing HWModel*/
this.firstOccuranceOfHwModelInModelScope = new AbstractMap.SimpleEntry<>(file,
(Element) object);
return this.firstOccuranceOfHwModelInModelScope;
}
}
/*- this is the case where HW model is not found*/
// creating HWModel in the first input AMALTHEA model file
final File firstInputModelFile = sortedInputFileSet.first();
final Document document = this.fileDocumentMapping.get(firstInputModelFile);
if (document != null) {
final Element rootElement = document.getRootElement();
if (rootElement.getName().equals("Amalthea")) {
final Element hwModel = new Element("hwModel");
rootElement.addContent(hwModel);
final Map<String, Object> map = cacheMap.get(firstInputModelFile);
/*- updating the cace with created HwModel*/
map.put(HwElementsCacheEnum.HwModel.name(), hwModel);
/*- returning Entry object with key as file (containing HW Model), value as the org.jdom2.Element representing HWModel*/
this.firstOccuranceOfHwModelInModelScope = new AbstractMap.SimpleEntry<>(
firstInputModelFile, hwModel);
return this.firstOccuranceOfHwModelInModelScope;
}
}
return null;
}
/**
* This method is used to check if the HW Element (with a specific name) is existing in the cache i.e. if the
* element is defined in HW Model
*
* @param type
* Type of HW Element
* @param hwElementName
* name of the HW Element
* @return boolean true if for the supplied type & name there exists a element in HW Model (of model scope)
*/
@SuppressWarnings("unchecked")
private boolean isHWElementPresentInCache(final HwElementsCacheEnum type, final String hwElementName) {
final Map<File, Map<String, Object>> cacheMap = this.cache.getCacheMap();
final Set<File> fileSet = this.fileDocumentMapping.keySet();
for (final File file : fileSet) {
final Map<String, Object> map = cacheMap.get(file);
final Object object = map.get(type.name());
if (object instanceof List) {
if (((List<String>) object).contains(hwElementName)) {
return true;
}
}
}
return false;
}
/**
* This method is used to get the HwElementsCacheBuilder object
* @param caches The list of all caches.
* @return HwElementsCacheBuilder
*/
private HwElementsCacheBuilder getHwElementsCacheBuilder(List<ICache> caches) {
if (caches != null) {
for (final ICache c : caches) {
if (c instanceof HwElementsCacheBuilder) {
return (HwElementsCacheBuilder) c;
}
}
}
return null;
}
private String encodeName(final String name) {
if (name == null || name.length() == 0) {
return "no-name";
}
String result;
try {
result = URLEncoder.encode(name, StandardCharsets.UTF_8.toString());
}
catch (final UnsupportedEncodingException e) {
result = name; // keep old name - we have no better option
}
return result;
}
/**
* This method is used to verify if the HW Elements definition is present in the supplied input file
*
* @param elementName
* Name of the HW element (e.g: Core name or Memory name)
* @param inputFile
* Input File which is being migrated to version 0.7.1
* @return boolean true if the Element definition is found in the supplied input model file
*/
@SuppressWarnings("unchecked")
private boolean isElementDefinedInFile(final String elementName, final HwElementsCacheEnum elementsType,
final File inputFile) {
final Map<File, Map<String, Object>> cacheMap = this.cache.getCacheMap();
final Map<String, Object> map = cacheMap.get(inputFile);
if (map != null) {
final Object object = map.get(elementsType.name());
if (object instanceof List) {
if (((List<String>) object).contains(elementName)) {
return true;
}
}
}
return false;
}
}