/** | |
******************************************************************************** | |
* 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; | |
} | |
} |