blob: 18c533886f6485f8249bba713dada1528c4daf55 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.api.atfxadapter.transaction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import org.asam.ods.AoException;
import org.asam.ods.ApplicationAttribute;
import org.asam.ods.ApplicationElement;
import org.asam.ods.ApplicationRelation;
import org.asam.ods.ApplicationStructure;
import org.asam.ods.BaseAttribute;
import org.asam.ods.BaseElement;
import org.asam.ods.BaseStructure;
import org.asam.ods.DataType;
import org.asam.ods.RelationRange;
import org.eclipse.mdm.api.base.ServiceNotProvidedException;
import org.eclipse.mdm.api.base.adapter.EntityType;
import org.eclipse.mdm.api.base.model.ContextType;
import org.eclipse.mdm.api.base.model.Entity;
import org.eclipse.mdm.api.base.model.Unit;
import org.eclipse.mdm.api.base.query.DataAccessException;
import org.eclipse.mdm.api.base.query.Filter;
import org.eclipse.mdm.api.base.query.ComparisonOperator;
import org.eclipse.mdm.api.base.query.Query;
import org.eclipse.mdm.api.base.query.QueryService;
import org.eclipse.mdm.api.base.query.Result;
import org.eclipse.mdm.api.dflt.model.CatalogAttribute;
import org.eclipse.mdm.api.dflt.model.CatalogComponent;
import org.eclipse.mdm.api.dflt.model.CatalogSensor;
import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
import org.eclipse.mdm.api.odsadapter.utils.ODSEnumerations;
import org.eclipse.mdm.api.odsadapter.utils.ODSUtils;
/**
* Used to create, update or delete {@link CatalogComponent},
* {@link CatalogSensor} and {@link CatalogAttribute} entities. Modifications of
* the listed types results in modifications of the application model.
*
* @see org.eclipse.mdm.api.odsadapter.transaction.CatalogManager
*/
final class CatalogManager {
private final ATFXTransaction transaction;
private ApplicationStructure applicationStructure;
private BaseStructure baseStructure;
/**
* Constructor.
*
* @param transaction
* The {@link ATFXTransaction}.
*/
CatalogManager(ATFXTransaction transaction) {
this.transaction = transaction;
}
/**
* Creates for each given {@link CatalogComponent} a corresponding
* application element including all required application relations.
*
* @param catalogComponents
* The {@code CatalogComponent}s.
* @throws AoException
* Thrown in case of errors.
*/
public void createCatalogComponents(Collection<CatalogComponent> catalogComponents) throws AoException {
Map<ContextType, List<CatalogComponent>> catalogComponentsByContextType = catalogComponents.stream()
.collect(Collectors.groupingBy(CatalogComponent::getContextType));
for (Entry<ContextType, List<CatalogComponent>> entry : catalogComponentsByContextType.entrySet()) {
String odsContextTypeName = ODSUtils.CONTEXTTYPES.get(entry.getKey());
ApplicationElement contextRootApplicationElement = getApplicationStructure()
.getElementByName(odsContextTypeName);
BaseElement contextRootBaseElement = contextRootApplicationElement.getBaseElement();
ApplicationElement contextTemplateComponentApplicationElement = getApplicationStructure()
.getElementByName("Tpl" + odsContextTypeName + "Comp");
BaseElement baseElement = getBaseStructure().getElementByType("Ao" + odsContextTypeName + "Part");
for (CatalogComponent catalogComponent : entry.getValue()) {
ApplicationElement applicationElement = createApplicationElement(catalogComponent.getName(),
baseElement);
// relation context root to context component
ApplicationRelation applicationRelation = getApplicationStructure().createRelation();
applicationRelation.setElem1(contextRootApplicationElement);
applicationRelation.setElem2(applicationElement);
applicationRelation.setRelationName(catalogComponent.getName());
applicationRelation.setInverseRelationName(odsContextTypeName);
applicationRelation
.setBaseRelation(getBaseStructure().getRelation(contextRootBaseElement, baseElement));
applicationRelation._release();
// relation template component to context component
applicationRelation = getApplicationStructure().createRelation();
applicationRelation.setElem1(contextTemplateComponentApplicationElement);
applicationRelation.setElem2(applicationElement);
applicationRelation.setRelationName(catalogComponent.getName());
applicationRelation.setInverseRelationName("Tpl" + odsContextTypeName + "Comp");
applicationRelation.setRelationRange(new RelationRange((short) 0, (short) -1));
applicationRelation.setInverseRelationRange(new RelationRange((short) 1, (short) 1));
// release resources
applicationElement._release();
applicationRelation._release();
}
// release resources
contextTemplateComponentApplicationElement._release();
contextRootApplicationElement._release();
contextRootBaseElement._release();
baseElement._release();
}
}
/**
* Creates for each given {@link CatalogSensor} a corresponding application
* element including all required application relations.
*
* @param catalogSensors
* The {@code CatalogSensor}s.
* @throws AoException
* Thrown in case of errors.
*/
public void createCatalogSensors(Collection<CatalogSensor> catalogSensors) throws AoException {
Map<String, List<CatalogSensor>> catalogSensorsByCatalogComponent = catalogSensors.stream()
.collect(Collectors.groupingBy(cs -> cs.getCatalogComponent().getName()));
ApplicationElement channelApplicationElement = getApplicationStructure().getElementByName("MeaQuantity");
BaseElement channelBaseElement = channelApplicationElement.getBaseElement();
for (Entry<String, List<CatalogSensor>> entry : catalogSensorsByCatalogComponent.entrySet()) {
ApplicationElement contextComponentApplicationElement = getApplicationStructure()
.getElementByName(entry.getKey());
BaseElement contextComponentBaseElement = contextComponentApplicationElement.getBaseElement();
ApplicationElement contextTemplateSensorApplicationElement = getApplicationStructure()
.getElementByName("TplSensor");
BaseElement baseElement = getBaseStructure().getElementByType("AoTestEquipmentPart");
for (CatalogSensor catalogSensor : entry.getValue()) {
ApplicationElement applicationElement = createApplicationElement(catalogSensor.getName(), baseElement);
// relation context component to context sensor
ApplicationRelation applicationRelation = getApplicationStructure().createRelation();
applicationRelation.setElem1(contextComponentApplicationElement);
applicationRelation.setElem2(applicationElement);
applicationRelation.setRelationName(catalogSensor.getName());
applicationRelation.setInverseRelationName(entry.getKey());
applicationRelation
.setBaseRelation(getBaseStructure().getRelation(contextComponentBaseElement, baseElement));
applicationRelation._release();
// relation template sensor to context sensor
applicationRelation = getApplicationStructure().createRelation();
applicationRelation.setElem1(contextTemplateSensorApplicationElement);
applicationRelation.setElem2(applicationElement);
applicationRelation.setRelationName(catalogSensor.getName());
applicationRelation.setInverseRelationName("TplSensor");
applicationRelation.setRelationRange(new RelationRange((short) 0, (short) -1));
applicationRelation.setInverseRelationRange(new RelationRange((short) 1, (short) 1));
applicationRelation._release();
// relation channel to context sensor
applicationRelation = getApplicationStructure().createRelation();
applicationRelation.setElem1(channelApplicationElement);
applicationRelation.setElem2(applicationElement);
applicationRelation.setRelationName(catalogSensor.getName());
applicationRelation.setInverseRelationName("MeaQuantity");
applicationRelation.setBaseRelation(getBaseStructure().getRelation(channelBaseElement, baseElement));
// release resources
applicationElement._release();
applicationRelation._release();
}
// release resources
contextComponentApplicationElement._release();
contextTemplateSensorApplicationElement._release();
contextComponentBaseElement._release();
baseElement._release();
}
// release resources
channelApplicationElement._release();
channelBaseElement._release();
}
/**
* Creates for each given {@link CatalogAttribute} a corresponding
* application attribute.
*
* @param catalogAttributes
* The {@code CatalogAttribute}s.
* @throws AoException
* Thrown in case of errors.
*/
public void createCatalogAttributes(Collection<CatalogAttribute> catalogAttributes) throws AoException {
Map<String, List<CatalogAttribute>> catalogAttributesByCatalogComponent = catalogAttributes.stream()
.collect(Collectors.groupingBy(CatalogManager::getParentName));
for (Entry<String, List<CatalogAttribute>> entry : catalogAttributesByCatalogComponent.entrySet()) {
ApplicationElement applicationElement = getApplicationStructure().getElementByName(entry.getKey());
for (CatalogAttribute catalogAttribute : entry.getValue()) {
ApplicationAttribute applicationAttribute = applicationElement.createAttribute();
DataType dataType = ODSUtils.VALUETYPES.get(catalogAttribute.getValueType());
applicationAttribute.setDataType(dataType);
applicationAttribute.setName(catalogAttribute.getName());
if (dataType == DataType.DT_ENUM) {
applicationAttribute.setEnumerationDefinition(getApplicationStructure().getEnumerationDefinition(
ODSEnumerations.getEnumName(catalogAttribute.getEnumerationObject())));
}
Optional<Unit> unit = catalogAttribute.getUnit();
if (unit.isPresent()) {
applicationAttribute.setUnit(ODSConverter.toODSID(unit.get().getID()));
}
// release resources
applicationAttribute._release();
}
// release resources
applicationElement._release();
}
}
/**
* Updates the application attribute for each given
* {@link CatalogAttribute}.
*
* @param catalogAttributes
* The {@code CatalogAttribute}s.
* @throws AoException
* Thrown in case of errors.
*/
public void updateCatalogAttributes(List<CatalogAttribute> catalogAttributes) throws AoException {
Map<String, List<CatalogAttribute>> catalogAttributesByCatalogComponent = catalogAttributes.stream()
.collect(Collectors.groupingBy(CatalogManager::getParentName));
for (Entry<String, List<CatalogAttribute>> entry : catalogAttributesByCatalogComponent.entrySet()) {
ApplicationElement applicationElement = getApplicationStructure().getElementByName(entry.getKey());
for (CatalogAttribute catalogAttribute : entry.getValue()) {
ApplicationAttribute applicationAttribute = applicationElement
.getAttributeByName(catalogAttribute.getName());
Optional<Unit> unit = catalogAttribute.getUnit();
if (unit.isPresent()) {
applicationAttribute.setUnit(ODSConverter.toODSID(unit.get().getID()));
}
// release resources
applicationAttribute._release();
}
// release resources
applicationElement._release();
}
}
/**
* Deletes the corresponding application element for each given
* {@link CatalogComponent}. Deleting a {@code CatalogComponent} is only
* allowed if it is not used in templates and all of its children could be
* deleted. So at first it is tried to delete its {@link CatalogAttribute}s
* and {@link CatalogSensor}s. On success it is ensured none of the given
* {@code
* CatalogComponent}s is used in templates. Finally the corresponding
* application elements are deleted.
*
* @param catalogComponents
* The {@code CatalogComponent}s.
* @throws AoException
* Thrown in case of errors.
* @throws DataAccessException
* Thrown in case of errors.
*/
public void deleteCatalogComponents(Collection<CatalogComponent> catalogComponents)
throws AoException, DataAccessException {
List<CatalogAttribute> attributes = new ArrayList<>();
List<CatalogSensor> sensors = new ArrayList<>();
for (CatalogComponent catalogComponent : catalogComponents) {
attributes.addAll(catalogComponent.getCatalogAttributes());
sensors.addAll(catalogComponent.getCatalogSensors());
}
transaction.delete(sensors);
transaction.delete(attributes);
if (areReferencedInTemplates(catalogComponents)) {
throw new DataAccessException(
"Unable to delete given catalog components since at least " + "one is used in templates.");
}
for (CatalogComponent catalogComponent : catalogComponents) {
ApplicationElement applicationElement = getApplicationStructure()
.getElementByName(catalogComponent.getName());
for (ApplicationRelation applicationRelation : applicationElement.getAllRelations()) {
getApplicationStructure().removeRelation(applicationRelation);
// release resources
applicationRelation._release();
}
getApplicationStructure().removeElement(applicationElement);
// release resources
applicationElement._release();
}
}
/**
* Deletes the corresponding application element for each given
* {@link CatalogSensor}. Deleting a {@code CatalogSensor} is only allowed
* if it is not used in templates and all of its children could be deleted.
* So at first it is tried to delete its {@link CatalogAttribute}s. On
* success it is ensured none of the given {@code CatalogSensor}s is used in
* templates. Finally the corresponding application elements are deleted.
*
* @param catalogSensors
* The {@code CatalogSensor}s.
* @throws AoException
* Thrown in case of errors.
* @throws DataAccessException
* Thrown in case of errors.
*/
public void deleteCatalogSensors(Collection<CatalogSensor> catalogSensors) throws AoException, DataAccessException {
List<CatalogAttribute> attributes = new ArrayList<>();
for (CatalogSensor catalogSensor : catalogSensors) {
attributes.addAll(catalogSensor.getCatalogAttributes());
}
transaction.delete(attributes);
if (areReferencedInTemplates(catalogSensors)) {
throw new DataAccessException(
"Unable to delete given catalog sensors since at " + "least one is used in templates.");
}
for (CatalogSensor catalogSensor : catalogSensors) {
ApplicationElement applicationElement = getApplicationStructure().getElementByName(catalogSensor.getName());
for (ApplicationRelation applicationRelation : applicationElement.getAllRelations()) {
getApplicationStructure().removeRelation(applicationRelation);
// release resources
applicationRelation._release();
}
getApplicationStructure().removeElement(applicationElement);
// release resources
applicationElement._release();
}
}
/**
* Deletes the corresponding application attributes for each given
* {@link CatalogAttribute}. Deleting a {@code CatalogAttribute} is only
* allowed if it is not used in templates. So at first it is ensured none of
* the given {@code CatalogAttribute}s is used in templates and finally the
* corresponding application attributes are deleted.
*
* @param catalogAttributes
* The {@code CatalogAttribute}s.
* @throws AoException
* Thrown in case of errors.
* @throws DataAccessException
* Thrown in case of errors.
*/
public void deleteCatalogAttributes(Collection<CatalogAttribute> catalogAttributes)
throws AoException, DataAccessException {
if (areReferencedInTemplates(catalogAttributes)) {
throw new DataAccessException(
"Unable to delete given catalog attributes since at least " + "one is used in templates.");
}
Map<String, List<CatalogAttribute>> catalogAttributesByParent = catalogAttributes.stream()
.collect(Collectors.groupingBy(CatalogManager::getParentName));
for (Entry<String, List<CatalogAttribute>> entry : catalogAttributesByParent.entrySet()) {
ApplicationElement applicationElement = getApplicationStructure().getElementByName(entry.getKey());
for (CatalogAttribute catalogAttribute : entry.getValue()) {
ApplicationAttribute applicationAttribute = applicationElement
.getAttributeByName(catalogAttribute.getName());
applicationElement.removeAttribute(applicationAttribute);
// release resources
applicationAttribute._release();
}
// release resources
applicationElement._release();
}
}
/**
* Releases cached resources.
*/
public void clear() {
if (applicationStructure != null) {
applicationStructure._release();
}
if (baseStructure != null) {
baseStructure._release();
}
}
/**
* Creates a new {@link ApplicationElement} with given name and
* {@link BaseElement}. The returned {@code ApplicationElement} will be
* created with the three mandatory {@link ApplicationAttribute}s for 'Id',
* 'Name' and 'MimeType'.
*
* @param name
* The name of the application element.
* @param baseElement
* The {@code BaseElement} the created {@code
* ApplicationElement} will be derived from.
* @return The created {@code ApplicationElement} is returned.
* @throws AoException
* Thrown in case of errors.
*/
private ApplicationElement createApplicationElement(String name, BaseElement baseElement) throws AoException {
ApplicationElement applicationElement = getApplicationStructure().createElement(baseElement);
applicationElement.setName(name);
// Id
ApplicationAttribute idApplicationAttribute = applicationElement.getAttributeByBaseName("id");
idApplicationAttribute.setName("Id");
idApplicationAttribute.setDataType(DataType.DT_LONGLONG);
idApplicationAttribute.setIsAutogenerated(true);
// Name
ApplicationAttribute nameApplicationAttribute = applicationElement.getAttributeByBaseName("name");
nameApplicationAttribute.setName("Name");
nameApplicationAttribute.setLength(50);
// MimeType
BaseAttribute mimeTypeBaseAttribute = baseElement.getAttributes("mime_type")[0];
ApplicationAttribute mimeTypeApplicationAttribute = applicationElement.createAttribute();
mimeTypeApplicationAttribute.setBaseAttribute(mimeTypeBaseAttribute);
mimeTypeApplicationAttribute.setName("MimeType");
mimeTypeApplicationAttribute.setDataType(DataType.DT_STRING);
mimeTypeApplicationAttribute.setLength(256);
mimeTypeApplicationAttribute.setIsObligatory(true);
// release resources
idApplicationAttribute._release();
nameApplicationAttribute._release();
mimeTypeApplicationAttribute._release();
mimeTypeBaseAttribute._release();
return applicationElement;
}
/**
* Returns the cached {@link ApplicationStructure}.
*
* @return The {@code ApplicationStructure} is returned.
* @throws AoException
* Thrown if unable to access the {@code
* ApplicationStructure}.
*/
private ApplicationStructure getApplicationStructure() throws AoException {
if (applicationStructure == null) {
applicationStructure = transaction.getModelManager().getAoSession().getApplicationStructure();
}
return applicationStructure;
}
/**
* Returns the cached {@link BaseStructure}.
*
* @return The {@code BaseStructure} is returned.
* @throws AoException
* Thrown if unable to access the {@code
* BaseStructure}.
*/
private BaseStructure getBaseStructure() throws AoException {
if (baseStructure == null) {
baseStructure = transaction.getModelManager().getAoSession().getBaseStructure();
}
return baseStructure;
}
/**
* Checks whether given {@link Entity}s are referenced in templates.
*
* @param entities
* The checked entities ({@link CatalogComponent},
* {@link CatalogSensor} or {@link CatalogAttribute}).
* @return Returns {@code true} if at least one entity is referenced in a
* template.
* @throws AoException
* Thrown on errors.
* @throws DataAccessException
* Thrown on errors.
*/
private boolean areReferencedInTemplates(Collection<? extends Entity> entities)
throws AoException, DataAccessException {
Map<EntityType, List<Entity>> entitiesByEntityType = entities.stream()
.collect(Collectors.groupingBy(transaction.getModelManager()::getEntityType));
for (Entry<EntityType, List<Entity>> entry : entitiesByEntityType.entrySet()) {
EntityType source = entry.getKey();
EntityType target = transaction.getModelManager().getEntityType(source.getName().replace("Cat", "Tpl"));
Query query = transaction.getContext().getQueryService()
.orElseThrow(() -> new ServiceNotProvidedException(QueryService.class))
.createQuery().selectID(target).join(source, target);
List<Result> results = query.fetch(Filter.and()
.add(ComparisonOperator.IN_SET.create(source.getIDAttribute(), collectInstanceIDs(entry.getValue()))));
if (results.size() > 0) {
return true;
}
}
return false;
}
/**
* Collect the instance IDs of all given {@link Entity}s.
*
* @param entities
* The {@link Entity}s.
* @return The instance IDs a {@code String[]} are turned.
*/
private static String[] collectInstanceIDs(List<Entity> entities) {
String[] ids = new String[entities.size()];
for (int i = 0; i < ids.length; i++) {
ids[i] = entities.get(i).getID();
}
return ids;
}
/**
* Returns the parent name for given {@link CatalogAttribute}.
*
* @param catalogAttribute
* The {@code CatalogAttribute}.
* @return The parent name is returned.
*/
private static String getParentName(CatalogAttribute catalogAttribute) {
Optional<CatalogComponent> catalogComponent = catalogAttribute.getCatalogComponent();
if (catalogComponent.isPresent()) {
return catalogComponent.get().getName();
}
return catalogAttribute.getCatalogSensor()
.orElseThrow(() -> new IllegalStateException("Parent entity is unknown.")).getName();
}
}