From 43c423ea613eb0234c8cf9299bf41605da819d2f Mon Sep 17 00:00:00 2001 From: Reino Ruusu Date: Fri, 25 Sep 2020 09:27:23 +0300 Subject: [PATCH] Utilities for checking component consistency against tech type data gitlab #96 Change-Id: I3039933e89a214eb29d8f339c5385ed1e5876b6c --- .../techtype/TechTypeValidationUtils.java | 133 +++++++++++++++--- 1 file changed, 117 insertions(+), 16 deletions(-) diff --git a/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeValidationUtils.java b/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeValidationUtils.java index a82f65a6..941036cd 100644 --- a/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeValidationUtils.java +++ b/org.simantics.district.network/src/org/simantics/district/network/techtype/TechTypeValidationUtils.java @@ -1,9 +1,12 @@ package org.simantics.district.network.techtype; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.simantics.Simantics; @@ -11,17 +14,22 @@ import org.simantics.databoard.binding.NumberBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.NumberType; +import org.simantics.databoard.type.StringType; import org.simantics.databoard.util.Range; import org.simantics.databoard.util.RangeException; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.Statement; import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.QueryIndexUtils; import org.simantics.db.layer0.request.PropertyInfo; import org.simantics.db.layer0.request.PropertyInfoRequest; import org.simantics.district.network.ontology.DistrictNetworkResource; +import org.simantics.district.network.techtype.requests.PossibleTechTypeKeyName; import org.simantics.district.network.techtype.requests.TechTypeTableData; import org.simantics.layer0.Layer0; import org.slf4j.Logger; @@ -86,23 +94,15 @@ public class TechTypeValidationUtils { continue; } - String valueString = values.get(propertyName).replace(",", "."); - try { - Double num = Double.valueOf(valueString); - NumberBinding binding = (NumberBinding)info.defaultBinding; - Number value = (Number) binding.create(num); - - if (!rng.contains(value)) { - LOGGER.trace(" {} - range violation {} / {}", propertyName, valueString, range); - result.add(code + "/" + propertyName); - } else { - LOGGER.trace(" {} - valid value {} / {}", propertyName, valueString, range); - } - } catch (NumberFormatException e) { + Number value = getPossibleNumericValue(info, values.get(propertyName)); + if (value == null) { // Nothing to do here, treat non-numeric strings as missing values - LOGGER.trace(" {} - no value {} / {}", propertyName, valueString, range); - } catch (BindingException e) { - LOGGER.error("Binding error for property {}", propertyName, e); + LOGGER.trace(" {} - no value {} / {}", propertyName, values.get(propertyName), range); + } else if (!rng.contains(value)) { + LOGGER.trace(" {} - range violation {} / {}", propertyName, values.get(propertyName), range); + result.add(code + "/" + propertyName); + } else { + LOGGER.trace(" {} - valid value {} / {}", propertyName, values.get(propertyName), range); } } } @@ -112,6 +112,107 @@ public class TechTypeValidationUtils { } }); } + + /** + * Find all component properties that do not match the value given in a tech type table. + * + * @param table A TechTypeTable instance + * @return Lists of all non-matching properties, indexed by component resource + * @throws DatabaseException + */ + public static Map> findConsistencyViolations(Resource table) throws DatabaseException { + LOGGER.trace("Validating resource table {}", table); + + // Use a unique read - we don't want to pollute the cache with this + return Simantics.getSession().syncRequest(new UniqueRead>>() { + @Override + public Map> perform(ReadGraph graph) throws DatabaseException { + Resource type = graph.getPossibleObject(table, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasComponentType); + if (type == null) + return Collections.emptyMap(); + + Resource model = graph.syncRequest(new IndexRoot(table)); + Map props = graph.syncRequest(new PropertyInfoMapOfType(type), TransientCacheListener.instance()); + + String keyName = graph.syncRequest(new PossibleTechTypeKeyName(type)); + PropertyInfo keyPredicate = props.get(keyName); + + if (keyName.startsWith("_")) + keyName = keyName.substring(1); + + Map> data = graph.syncRequest(new TechTypeTableData(table), TransientCacheListener.instance()); + Map> result = new HashMap<>(); + + for (Resource component : QueryIndexUtils.searchByType(graph, model, type)) { + String key = graph.getRelatedValue2(component, keyPredicate.predicate); + Map values = data.get(key); + if (values == null) { + // Highlight the missing tech type key + addMapListItem(result, component, keyPredicate); + continue; + } + + for (PropertyInfo prop : props.values()) { + if (!prop.isHasProperty) + continue; + + String valueString = values.get(prop.name); + if (valueString == null) + continue; + + Object value = null; + if (prop.requiredDatatype instanceof NumberType) { + value = getPossibleNumericValue(prop, valueString); + } else if (prop.requiredDatatype instanceof StringType) { + value = valueString; + } else { + continue; + } + + if (value == null) { + Statement statement = graph.getPossibleStatement(component, prop.predicate); + if (statement != null && statement.getObject().equals(component)) { + addMapListItem(result, component, prop); + } + + continue; + } + + Object currentValue = graph.getRelatedValue2(component, prop.predicate); + if (!Objects.equals(value, currentValue)) { + addMapListItem(result, component, prop); + } + } + } + + return result; + } + }); + } + + private static Number getPossibleNumericValue(PropertyInfo info, String valueString) { + try { + Double num = Double.valueOf(valueString.replace(",", ".")); + NumberBinding binding = (NumberBinding)info.defaultBinding; + Number value = (Number) binding.create(num); + return value; + } catch (NumberFormatException e) { + return null; + } catch (BindingException e) { + LOGGER.error("Binding error for property {}", info.name, e); + return null; + } + } + + private static void addMapListItem(Map> result, A a, B b) { + List list = result.get(a); + if (list == null) { + list = new ArrayList<>(); + result.put(a, list); + } + + list.add(b); + } private static class PropertyInfoMapOfType extends ResourceRead> { protected PropertyInfoMapOfType(Resource type) { -- 2.47.1