| /******************************************************************************** |
| * Copyright (c) 2015-2019 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.ComparisonOperator; |
| import org.eclipse.mdm.api.base.query.DataAccessException; |
| import org.eclipse.mdm.api.base.query.Filter; |
| 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(); |
| } |
| |
| } |