Merge branch 'mkoller/nodeprovider' into mkoller/webclient
Conflicts:
api/atfxadapter/openatfx/build.gradle
Change-Id: Ieab74c9222711b539e0f6d962208824631f54116
diff --git a/api/atfxadapter/openatfx/build.gradle b/api/atfxadapter/openatfx/build.gradle
index 912e114..c0a9e5c 100644
--- a/api/atfxadapter/openatfx/build.gradle
+++ b/api/atfxadapter/openatfx/build.gradle
@@ -31,8 +31,9 @@
compile project(":api:odsadapter")
testCompile 'junit:junit:4.12'
testCompile 'org.apache.commons:commons-math:2.2'
- testCompile 'org.assertj:assertj-core:3.6.2'
testCompile 'org.mockito:mockito-core:2.13.0'
+ testCompile 'org.assertj:assertj-core:3.6.2'
+
}
configurations.all {
@@ -64,9 +65,15 @@
outputs.dir file("${buildDir}/openatfx-${atfxVersion}")
}
+
task copySource(dependsOn: unzipOpenATFX, type: Copy) {
from file("${buildDir}/openatfx-${atfxVersion}/src")
into 'src'
+ /*
+ * openATFX is implemented against the ODS API compiled by JacORB. The JacORB IDL compiler generates SelItem#operator() while SunORB IDL compiler generates SelItem#_operator().
+ * Since openMDM uses SunORB to generate the ODS API from IDL, openATFX source code is missing SelItem#operator(). Therefore we change it with a simple replace and add the underscore.
+ */
+ filter { line -> line.replaceAll('SelOperator.AND.equals\\(condition.operator\\(\\)', 'SelOperator.AND.equals\\(condition._operator\\(\\)').replaceAll('return new TS_ValueSeq\\(new TS_UnionSeq\\(\\), new short\\[0\\]\\)', 'return ODSHelper.tsValue2tsValueSeq\\(new TS_Value\\[0\\], dt\\)') }
}
compileJava.dependsOn copySource
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/adapter/Attribute.java b/api/base/src/main/java/org/eclipse/mdm/api/base/adapter/Attribute.java
index 0771091..6d50c8c 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/adapter/Attribute.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/adapter/Attribute.java
@@ -108,7 +108,7 @@
* @return Created {@code Value} is returned.
*/
default Value createValue(String unit, Object input) {
- return createValue(unit, true, input);
+ return createValue(unit, input != null, input);
}
/**
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/query/ComparisonOperator.java b/api/base/src/main/java/org/eclipse/mdm/api/base/query/ComparisonOperator.java
index 8c80acd..d01417f 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/query/ComparisonOperator.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/query/ComparisonOperator.java
@@ -253,7 +253,7 @@
case IN_SET:
return "in";
case IS_NOT_NULL:
- return "not_in";
+ return "is_not_null";
case IS_NULL:
return "is_null";
case LESS_THAN:
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchQuery.java b/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchQuery.java
index 6be838a..dc05bf8 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchQuery.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchQuery.java
@@ -94,6 +94,8 @@
*/
List<Value> getFilterValues(Attribute attribute, Filter filter) throws DataAccessException;
+ List<Result> getFilterResults(List<Attribute> attributes, Filter filter) throws DataAccessException;
+
/**
* Executes this search query with given {@link EntityType}s. The {@code
* EntityType}s must be fully supported by this search query. This method
diff --git a/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchService.java b/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchService.java
index 4ea388e..5223f54 100644
--- a/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchService.java
+++ b/api/base/src/main/java/org/eclipse/mdm/api/base/search/SearchService.java
@@ -126,6 +126,8 @@
List<Value> getFilterValues(Class<? extends Entity> entityClass, Attribute attribute, Filter filter)
throws DataAccessException;
+ List<Result> getFilterResults(Class<? extends Entity> entityClass, List<Attribute> attributes, Filter filter) throws DataAccessException;
+
/**
* Executes the associated {@link SearchQuery} with given {@link EntityType}s.
* The {@code EntityType}s must be fully supported by the {@code SearchQuery}
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
index d3a34b7..d143f95 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
@@ -17,6 +17,7 @@
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -157,6 +158,28 @@
* {@inheritDoc}
*/
@Override
+ public List<Result> getFilterResults(List<Attribute> attributes, Filter filter) throws DataAccessException {
+ Query query = queryService.createQuery();
+ for (Attribute attribute : attributes) {
+ addJoins(query, attribute.getEntityType());
+ query.select(attribute, Aggregation.DISTINCT);
+ query.group(attribute);
+ }
+
+ // add required joins
+ filter.stream().filter(FilterItem::isCondition).map(FilterItem::getCondition).forEach(c -> {
+ addJoins(query, c.getAttribute().getEntityType());
+ });
+
+ addImplicitLocalColumnJoin(query);
+
+ return query.fetch(filter);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public List<Result> fetchComplete(List<EntityType> entityTypes, Filter filter) throws DataAccessException {
Query query = queryService.createQuery().selectID(modelManager.getEntityType(entityClass));
@@ -298,7 +321,8 @@
* @param entityType The target {@link EntityType}.
*/
private void addJoins(Query query, EntityType entityType) {
- if (query.isQueried(entityType)) {
+ EntityType searchQueryEntityType = modelManager.getEntityType(entityClass);
+ if (query.isQueried(entityType) || entityType.equals(searchQueryEntityType)) {
return;
}
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/MergedSearchQuery.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/MergedSearchQuery.java
index 4ace2fb..3b11123 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/MergedSearchQuery.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/MergedSearchQuery.java
@@ -17,8 +17,10 @@
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -30,6 +32,9 @@
import org.eclipse.mdm.api.base.adapter.Attribute;
import org.eclipse.mdm.api.base.adapter.EntityType;
import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.query.Aggregation;
+import org.eclipse.mdm.api.base.query.ComparisonOperator;
+import org.eclipse.mdm.api.base.query.Condition;
import org.eclipse.mdm.api.base.query.DataAccessException;
import org.eclipse.mdm.api.base.query.Filter;
import org.eclipse.mdm.api.base.query.Record;
@@ -97,7 +102,7 @@
return Stream.concat(orderValues.stream(), resultValues.stream())
// group by value and merge values
- .collect(groupingBy(v -> v.extract(), reducing((v1, v2) -> v1)))
+ .collect(groupingBy(Value::extract, reducing((v1, v2) -> v1)))
// collect merged results
.values().stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
}
@@ -106,6 +111,71 @@
* {@inheritDoc}
*/
@Override
+ public List<Result> getFilterResults(List<Attribute> attributes, Filter filter)
+ throws DataAccessException {
+ ContextState contextState = filter.getContext();
+ List<Result> firstResults;
+ List<Result> secondResults;
+
+ if (ContextState.ORDERED.equals(contextState)) {
+ firstResults = byOrder.getFilterResults(attributes, filter);
+ } else {
+ firstResults = byResult.getFilterResults(attributes, filter);
+ }
+
+ if (firstResults.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ Filter followUpFilter = Filter.or();
+ for (Result result : firstResults) {
+ Filter attributeValueFilter = Filter.or();
+ for (Attribute attr : attributes) {
+ Value value = result.getValue(attr, Aggregation.DISTINCT);
+ attributeValueFilter.add(ComparisonOperator.EQUAL.create(contextState, attr, value.extract(contextState)));
+ }
+ followUpFilter.merge(attributeValueFilter);
+ }
+
+ if (ContextState.ORDERED.equals(contextState)) {
+ secondResults = byResult.getFilterResults(attributes, followUpFilter);
+ } else {
+ secondResults = byOrder.getFilterResults(attributes, followUpFilter);
+ }
+
+ Set<EntityType> firstTypes = getTypesFromResults(firstResults);
+
+ Set<EntityType> secondTypes = getTypesFromResults(secondResults);
+
+
+ Set<EntityType> types = new HashSet<>(firstTypes);
+ types.addAll(secondTypes);
+
+ if (types.size() > 1) {
+ throw new DataAccessException("Cannot handle multiple types here");
+ }
+
+ return Stream.concat(firstResults.stream(), secondResults.stream())
+ .collect(groupingBy(r -> getAttributeValues(attributes, r, Aggregation.DISTINCT), reducing(Result::merge)))
+ .values().stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
+ }
+
+ private Set<EntityType> getTypesFromResults(List<Result> results) {
+ return results.stream()
+ .map(result -> result.stream().collect(Collectors.toList()))
+ .flatMap(Collection::stream)
+ .map(Record::getEntityType)
+ .collect(Collectors.toSet());
+ }
+
+ private String getAttributeValues(List<Attribute> attributes, Result result, Aggregation aggregation) {
+ return attributes.stream().map(attribute -> result.getValue(attribute, aggregation).extract()).map(String::valueOf).collect(Collectors.joining("_"));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public List<Result> fetchComplete(List<EntityType> entityTypes, Filter filter) throws DataAccessException {
return mergeResults(byOrder.fetchComplete(entityTypes, filter), byResult.fetchComplete(entityTypes, filter));
}
@@ -229,9 +299,13 @@
* @return The merged {@link Result} is returned.
*/
private List<Result> mergeResults(List<Result> results1, List<Result> results2) {
+ return mergeResults(entityType, Aggregation.NONE, results1, results2);
+ }
+
+ private List<Result> mergeResults(EntityType mergeType, Aggregation aggregation, List<Result> results1, List<Result> results2) {
return Stream.concat(results1.stream(), results2.stream())
// group by instance ID and merge grouped results
- .collect(groupingBy(r -> getMergeId(r.getRecord(entityType)), reducing(Result::merge)))
+ .collect(groupingBy(r -> getMergeId(mergeType, r.getRecord(mergeType), aggregation), reducing(Result::merge)))
// collect merged results
.values().stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
}
@@ -244,12 +318,23 @@
* @return Returns the ID of the record, if ID of ContextState.MEASURED is not
* set, returns ID of ContextState.ORDERED.
*/
- private String getMergeId(Record rec) {
- if (rec.getID() == null) {
- // MEASURED (default) ContextState is null, so we use the ID stored in ORDERED
- return rec.getValues().get(entityType.getIDAttribute().getName()).extract(ContextState.ORDERED);
+ private String getMergeId(EntityType idType, Record rec, Aggregation aggregation) {
+
+ String idKey;
+ if (Aggregation.NONE == aggregation) {
+ idKey = idType.getIDAttribute().getName();
} else {
- return rec.getID();
+ idKey = String.format("%s(%s)", aggregation.name(), idType.getIDAttribute().getName());
+ }
+
+ Value idValue = rec.getValues().get(idKey);
+ String id = idValue.extract();
+
+ if (id == null) {
+ // MEASURED (default) ContextState is null, so we use the ID stored in ORDERED
+ return rec.getValues().get(idKey).extract(ContextState.ORDERED);
+ } else {
+ return id;
}
}
diff --git a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/ODSSearchService.java b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/ODSSearchService.java
index 2725cbf..8757ed8 100644
--- a/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/ODSSearchService.java
+++ b/api/odsadapter/src/main/java/org/eclipse/mdm/api/odsadapter/search/ODSSearchService.java
@@ -124,6 +124,13 @@
return findSearchQuery(entityClass).getFilterValues(attribute, filter);
}
+ @Override
+ public List<Result> getFilterResults(final Class<? extends Entity> entityClass,
+ final List<Attribute> attributes,
+ final Filter filter) throws DataAccessException {
+ return findSearchQuery(entityClass).getFilterResults(attributes, filter);
+ }
+
/**
* {@inheritDoc}
*/
@@ -155,7 +162,9 @@
EntityType entityType = context.getODSModelManager().getEntityType(entityClass);
Map<String, Result> recordsByEntityID = new HashMap<>();
- for (Result result : findSearchQuery(entityClass).fetch(attributes, mergedFilter)) {
+ SearchQuery searchQuery = findSearchQuery(entityClass);
+ List<Result> results = searchQuery.fetch(attributes, mergedFilter);
+ for (Result result : results) {
recordsByEntityID.put(result.getRecord(entityType).getID(), result);
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java
index b52d6aa..687cc17 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java
@@ -14,12 +14,15 @@
package org.eclipse.mdm.businessobjects.control;
+import static java.util.stream.Collectors.joining;
+
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BaseErrorListener;
@@ -254,10 +257,9 @@
final Predicate<EntityType> filterByEntityTypeName = entityType -> ServiceUtils
.workaroundForTypeMapping(entityType).equals(typeName);
- return availableEntityTypes.stream()
- .filter(filterByEntityTypeName)
- .findAny()
- .orElseThrow(() -> new IllegalArgumentException("Entity " + typeName + " not found in data source!"))
+ return availableEntityTypes.stream().filter(filterByEntityTypeName).findAny()
+ .orElseThrow(
+ () -> new IllegalArgumentException("Entity " + typeName + " not found in data source!"))
.getAttribute(attributeName);
}
@@ -483,8 +485,10 @@
private static String toString(Condition condition) {
StringBuilder builder = new StringBuilder();
- if (condition.getContextState() != null && condition.getContextState().isOrdered()) {
- builder.append("ordered.");
+ ContextState contextState = condition.getContextState();
+ if (contextState != null) {
+ builder.append(contextState.toString());
+ builder.append(".");
}
builder.append(ServiceUtils.workaroundForTypeMapping(condition.getAttribute().getEntityType()));
@@ -494,10 +498,17 @@
builder.append(condition.getComparisonOperator());
builder.append(" ");
- if (condition.getValue().getValueType().isNumericalType()) {
- builder.append("" + condition.getValue().extract());
- } else {
- builder.append("\"" + condition.getValue().extract() + "\"");
+ boolean isValid = condition.getValue().isValid();
+
+ if (isValid) {
+ if (condition.getValue().getValueType().isNumericalType()) {
+ builder.append("" + condition.getValue().extract());
+ } else if (condition.getValue().getValueType().isSequence()) {
+ builder.append(Stream.of(condition.getValue().extract()).map(Object::toString)
+ .collect(joining("', '", "('", "')")));
+ } else {
+ builder.append("\"" + condition.getValue().extract() + "\"");
+ }
}
return builder.toString();
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/AttributeContainer.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/AttributeContainer.java
new file mode 100644
index 0000000..07c8e74
--- /dev/null
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/AttributeContainer.java
@@ -0,0 +1,36 @@
+package org.eclipse.mdm.nodeprovider.control;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.search.ContextState;
+
+public class AttributeContainer {
+
+ private ContextState contextState;
+ private Attribute attribute;
+ private boolean valid;
+ private Object value;
+
+ public AttributeContainer(final ContextState contextState, final Attribute attribute, final boolean valid,
+ final Object value) {
+ this.contextState = contextState;
+ this.attribute = attribute;
+ this.valid = valid;
+ this.value = value;
+ }
+
+ public ContextState getContextState() {
+ return contextState;
+ }
+
+ public Attribute getAttribute() {
+ return attribute;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/DefaultNodeProvider.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/DefaultNodeProvider.java
index 7062bfd..7c93adb 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/DefaultNodeProvider.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/DefaultNodeProvider.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.control;
import java.util.ArrayList;
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProvider.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProvider.java
index 75ad230..6527355 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProvider.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProvider.java
@@ -1,10 +1,27 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.control;
+import static java.util.stream.Collectors.toMap;
+
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.mdm.api.base.ServiceNotProvidedException;
@@ -21,6 +38,7 @@
import org.eclipse.mdm.api.base.model.TestStep;
import org.eclipse.mdm.api.base.model.Value;
import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.Aggregation;
import org.eclipse.mdm.api.base.query.BooleanOperator;
import org.eclipse.mdm.api.base.query.ComparisonOperator;
import org.eclipse.mdm.api.base.query.Filter;
@@ -34,13 +52,13 @@
import org.eclipse.mdm.api.dflt.model.Pool;
import org.eclipse.mdm.api.dflt.model.Project;
import org.eclipse.mdm.businessobjects.control.FilterParser;
-import org.eclipse.mdm.businessobjects.utils.Serializer;
import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
import org.eclipse.mdm.connector.boundary.ConnectorService;
import org.eclipse.mdm.nodeprovider.entity.NodeLevel;
import org.eclipse.mdm.nodeprovider.entity.NodeProvider;
import org.eclipse.mdm.nodeprovider.entity.NodeProviderRoot;
import org.eclipse.mdm.nodeprovider.entity.Order;
+import org.eclipse.mdm.nodeprovider.entity.ValuePrecision;
import org.eclipse.mdm.nodeprovider.utils.SerializationUtil;
import org.eclipse.mdm.protobuf.Mdm.Node;
@@ -57,6 +75,8 @@
private ConnectorService connectorService;
private NodeProviderRoot root;
+
+ private MDMExpressionLanguageService elService;
/**
* Construct a new {@link GenericNodeProvider} using the given
@@ -65,9 +85,10 @@
* @param connectorService
* @param root
*/
- public GenericNodeProvider(ConnectorService connectorService, NodeProviderRoot root) {
+ public GenericNodeProvider(ConnectorService connectorService, NodeProviderRoot root, MDMExpressionLanguageService expressionLanguageService) {
this.connectorService = connectorService;
this.root = root;
+ this.elService = expressionLanguageService;
}
/**
@@ -111,17 +132,14 @@
@Override
public List<Node> getTreePath(Node node) {
ApplicationContext context = this.connectorService.getContextByName(node.getSource());
-
NodeLevel nodeLevel = root.getNodeLevel(context, node.getType(), node.getIdAttribute());
-
List<Node> nodes = new ArrayList<>();
// reload node to get correct label
-
Query query = createQueryForNodeLevel(context, nodeLevel);
-
+
for (Result r : query.fetch(createFilterFromIdOrLabel(node, nodeLevel))) {
- nodes.add(convertNode(r, node.getSource(), nodeLevel, Filter.and()));
+ nodes.add(convertNode(r, node.getSource(), nodeLevel, Filter.and(), Aggregation.NONE));
}
if (nodes.size() != 1) {
@@ -153,7 +171,7 @@
if (isEnvironment(childNodeLevel.getEntityType())) {
return createQueryForNodeLevel(context, childNodeLevel).fetch().stream()
- .map(r -> convertNode(r, sourceName, childNodeLevel, filter)).collect(Collectors.toList());
+ .map(result -> convertNode(result, sourceName, childNodeLevel, filter, Aggregation.NONE)).collect(Collectors.toList());
}
if (!sourceName.equalsIgnoreCase(parent.getSource()) || !sourceNameMatches(filter, sourceName)) {
@@ -162,15 +180,15 @@
if (childNodeLevel.isVirtual()) {
// We use the TestStep entity as query root to request the values for the label
- List<Value> values = getSearchService(context).getFilterValues(TestStep.class,
- childNodeLevel.getLabelAttribute());
-
+ List<Result> results = getSearchService(context).getFilterResults(TestStep.class, childNodeLevel.getLabelAttributes(), filter);
+
// Filter values are always in measured
- List<Node> nodes = new ArrayList<>();
- for (Value v : values) {
- nodes.add(convertNode(v, sourceName, childNodeLevel, filter));
- }
- return nodes;
+ return results.stream()
+ .map(result -> convertLabelAttributesToContainers(result, Aggregation.DISTINCT, childNodeLevel))
+ .collect(toMap(v -> createBasicLabel(childNodeLevel.getContextState(), v), v -> v, (v1, v2) -> v1))
+ .values().stream()
+ .map(containers -> convertNodeByLabel(containers, sourceName, childNodeLevel, filter))
+ .collect(Collectors.toList());
} else {
return fetchNodes(context, childNodeLevel, filter);
}
@@ -201,7 +219,7 @@
Query query = createQueryForNodeLevel(context, parentLevel);
for (Result r : query.fetch()) {
- return convertNode(r, context.getSourceName(), parentLevel, childFilter);
+ return convertNode(r, context.getSourceName(), parentLevel, childFilter, Aggregation.NONE);
}
return null;
}
@@ -209,12 +227,10 @@
List<Node> nodes = new ArrayList<>();
if (parentLevel.isVirtual()) {
// We use the TestStep entity as query root to request the values for the label
- List<Result> results = getSearchService(context).fetchResults(TestStep.class,
- Arrays.asList(parentLevel.getLabelAttribute()), childFilter, "");
+ List<Result> results = getSearchService(context).fetchResults(TestStep.class, parentLevel.getLabelAttributes(), childFilter, "");
for (Result r : results) {
- Value value = r.getValue(parentLevel.getLabelAttribute());
- nodes.add(convertNode(value, node.getSource(), parentLevel, childFilter));
+ nodes.add(convertNode(r, node.getSource(), parentLevel, childFilter, Aggregation.NONE));
break;
}
} else {
@@ -238,7 +254,7 @@
private Filter createFilterFromIdOrLabel(Node node, NodeLevel nodeLevel) {
if (nodeLevel.isVirtual()) {
return Filter.and().add(ComparisonOperator.EQUAL.create(nodeLevel.getContextState(),
- nodeLevel.getLabelAttribute(), node.getLabel()));
+ getLabelAttribute(nodeLevel), node.getLabel()));
} else {
return Filter.and().id(nodeLevel.getEntityType(), node.getId());
}
@@ -281,12 +297,12 @@
*/
private List<Node> fetchNodes(ApplicationContext context, NodeLevel nodeLevel, Filter filter) {
- Class<? extends Entity> entityClass = getEntityClass(nodeLevel.getIdAttribute().getEntityType());
+ Class<? extends Entity> entityClass = getEntityClass(getFilterAttribute(nodeLevel).getEntityType());
- List<Result> results = getSearchService(context).fetchResults(entityClass,
- Arrays.asList(nodeLevel.getIdAttribute(), nodeLevel.getLabelAttribute()), filter, "");
+ List<Attribute> attributes = getAttributesFromNodeLevel(nodeLevel);
+ List<Result> results = getSearchService(context).fetchResults(entityClass, attributes, filter, "");
- return results.stream().map(r -> convertNode(r, context.getSourceName(), nodeLevel, filter))
+ return results.stream().map(r -> convertNode(r, context.getSourceName(), nodeLevel, filter, Aggregation.NONE))
.collect(Collectors.toList());
}
@@ -318,7 +334,7 @@
case "Quantity":
return Quantity.class;
default:
- throw new RuntimeException("Could not find entity class for entity with name '" + entityName + "'.");
+ throw new NodeProviderException("Could not find entity class for entity with name '" + entityName + "'.");
}
}
@@ -373,61 +389,108 @@
/**
* Converts a Result to a Node
*
- * @param r Result to convert
+ * @param result Result to convert
* @param sourceName name of the source
* @param nodeLevel NodeLevel
* @param parentFilter Filter of the parent Node
* @return Result converted to a Node
*/
- private Node convertNode(Result r, String sourceName, NodeLevel nodeLevel, Filter parentFilter) {
- String label = r.getValue(nodeLevel.getLabelAttribute()).extract(nodeLevel.getContextState());
- String id = r.getValue(nodeLevel.getIdAttribute()).extract(nodeLevel.getContextState());
+ private Node convertNode(Result result, String sourceName, NodeLevel nodeLevel, Filter parentFilter, Aggregation aggregation) {
+
+ ContextState contextState = nodeLevel.getContextState();
- Filter newFilter = null;
+ List<AttributeContainer> labelAttrContainers = convertToAttributeContainers(contextState, result, aggregation, nodeLevel.getLabelAttributes(), nodeLevel.getValuePrecision());
- if (parentFilter != null) {
- if (isEnvironment(nodeLevel.getEntityType())) {
- newFilter = parentFilter.copy();
- } else {
- newFilter = parentFilter.copy().add(
- ComparisonOperator.EQUAL.create(nodeLevel.getContextState(), nodeLevel.getIdAttribute(), id));
- }
- }
+ String label = createLabel(nodeLevel, labelAttrContainers);
- return SerializationUtil.createNode(sourceName,
- ServiceUtils.workaroundForTypeMapping(nodeLevel.getEntityType()), id,
- nodeLevel.getIdAttribute().getName(), newFilter, label);
+ Value idValue = result.getValue(nodeLevel.getEntityType().getIDAttribute(), aggregation);
+ String id = idValue.extract();
+
+ List<AttributeContainer> filterAttrContainers = convertToAttributeContainers(contextState, result, aggregation, nodeLevel.getFilterAttributes(), nodeLevel.getValuePrecision());
+ Filter newFilter = getNewNodeFilter(contextState, nodeLevel, parentFilter, filterAttrContainers);
+
+ return convertNode(id, label, sourceName, nodeLevel.getEntityType(), getFilterAttribute(nodeLevel).getName(), newFilter);
}
/**
- * Converts a Value to a Node.
- *
- * @param v value to convert
+ * Converts a Result to a Node
+ *
+ * @param result Result to convert
* @param sourceName name of the source
* @param nodeLevel NodeLevel
* @param parentFilter Filter of the parent Node
- * @return Value converted to a Node
+ * @return Result converted to a Node
*/
- private Node convertNode(Value v, String sourceName, NodeLevel nodeLevel, Filter parentFilter) {
+ private Node convertNodeByLabel(List<AttributeContainer> labelAttrContainers, String sourceName, NodeLevel nodeLevel, Filter parentFilter) {
+ ContextState contextState = nodeLevel.getContextState();
- ContextState contextState = nodeLevel.getContextState() == null ? ContextState.MEASURED
- : nodeLevel.getContextState();
+ String label = createLabel(nodeLevel, labelAttrContainers);
- String label = Serializer.serializeValue(v).toString();
+ /**
+ * TODO jst, 05.02.2021: This is counterintuitive. LabelAttributes are used for filtering..
+ */
+ Filter newFilter = getNewNodeFilter(contextState, nodeLevel, parentFilter, labelAttrContainers);
- Filter newFilter = null;
+ return convertNode(null, label, sourceName, nodeLevel.getEntityType(), getFilterAttribute(nodeLevel).getName(), newFilter);
+ }
- if (isEnvironment(nodeLevel.getEntityType())) {
- // only 1 environment per context -> no parent filter needed
- newFilter = Filter.and();
- } else if (parentFilter != null) {
- newFilter = parentFilter.copy()
- .add(ComparisonOperator.EQUAL.create(contextState, nodeLevel.getLabelAttribute(), v.extract()));
+ /**
+ * Creates label for {@link NodeLevel} respecting the NodeLevels {@link ValueExpression}.
+ *
+ * @param nodeLevel the {@link NodeLevel}
+ * @param attributeContainers the label attributes
+ * @return
+ */
+ private String createLabel(NodeLevel nodeLevel, List<AttributeContainer> attributeContainers) {
+ String label;
+ if (nodeLevel.getLabelExpression() == null) {
+ label = createBasicLabel(nodeLevel.getContextState(), attributeContainers);
+ } else {
+ label = elService.evaluateValueExpression(nodeLevel.getLabelExpression(), attributeContainers);
}
+ return label;
+ }
- return SerializationUtil.createNode(sourceName,
- ServiceUtils.workaroundForTypeMapping(nodeLevel.getEntityType()), label,
- nodeLevel.getIdAttribute().getName(), newFilter, label);
+ /**
+ * Creates a basic label for {@link NodeLevel} of label attributes, while *ignoring* the NodeLevels {@link ValueExpression}.
+ *
+ * @param contextState
+ * @param attributeContainers the label attributes
+ * @return
+ */
+ private String createBasicLabel(ContextState contextState, List<AttributeContainer> attributeContainers) {
+ String labelValue = attributeContainers.stream()
+ .map(this::mapAttributeContainerValue)
+ .collect(Collectors.joining(" "));
+
+ if (contextState == null) {
+ return labelValue;
+ } else {
+ return String.format("%s %s", contextState, labelValue);
+ }
+ }
+
+ private List<AttributeContainer> convertLabelAttributesToContainers(Result result, Aggregation aggregation, NodeLevel nodeLevel) {
+ return convertToAttributeContainers(nodeLevel.getContextState(), result, aggregation, nodeLevel.getLabelAttributes(), nodeLevel.getValuePrecision());
+ }
+
+ private List<AttributeContainer> convertToAttributeContainers(ContextState contextState, Result result, Aggregation aggregation, List<Attribute> attributes, ValuePrecision precision) {
+ return attributes.stream().map(attribute -> {
+ Value value = result.getValue(attribute, aggregation);
+ precision.applyOn(result.getValue(attribute, aggregation));
+ Object extractedValue = value.extract(contextState);
+ boolean valid = value.isValid(contextState);
+ return new AttributeContainer(contextState, attribute, valid, extractedValue);
+ }).collect(Collectors.toList());
+ }
+
+ private String mapAttributeContainerValue(AttributeContainer attributeContainer) {
+ if (attributeContainer.isValid()) {
+ String valueAsString = String.valueOf(attributeContainer.getValue());
+ return String.format("%s", valueAsString);
+ } else {
+ return String.format("%s=invalid", attributeContainer.getAttribute().getName());
+ }
}
/**
@@ -438,15 +501,50 @@
* @return Query for the given NodeLevel
*/
private Query createQueryForNodeLevel(ApplicationContext context, NodeLevel nodeLevel) {
+ List<Attribute> attributes = getAttributesFromNodeLevel(nodeLevel);
Query query = context.getQueryService().orElseThrow(() -> new ServiceNotProvidedException(QueryService.class))
- .createQuery().select(nodeLevel.getIdAttribute(), nodeLevel.getLabelAttribute())
- .group(nodeLevel.getIdAttribute(), nodeLevel.getLabelAttribute());
+ .createQuery()
+ .select(attributes)
+ .group(attributes);
- nodeLevel.getOrderAttributes().stream()
- .forEach(oa -> query.order(oa.getAttribute(), oa.getOrder() == Order.ASCENDING));
+ nodeLevel.getOrderAttributes().forEach(oa -> query.order(oa.getAttribute(), oa.getOrder() == Order.ASCENDING));
return query;
}
+
+ private List<Attribute> getAttributesFromNodeLevel(NodeLevel nodeLevel) {
+ Set<Attribute> attributes = new HashSet<>();
+ attributes.addAll(nodeLevel.getFilterAttributes());
+ attributes.addAll(nodeLevel.getLabelAttributes());
+
+ return new ArrayList<>(attributes);
+ }
+
+ private Node convertNode(String id, String label, String sourceName, EntityType entityType, String idAttr, Filter newFilter) {
+ return SerializationUtil.createNode(sourceName,
+ ServiceUtils.workaroundForTypeMapping(entityType), id,
+ idAttr, newFilter, label);
+ }
+
+ private Filter getNewNodeFilter(ContextState contextState, NodeLevel nodeLevel, Filter parentFilter, List<AttributeContainer> attributeContainers) {
+ Filter newFilter = null;
+
+ if (isEnvironment(nodeLevel.getEntityType())) {
+ // only 1 environment per context -> no parent filter needed
+ newFilter = Filter.and();
+ } else if (parentFilter != null) {
+ newFilter = parentFilter.copy();
+
+ for (AttributeContainer attributeContainer : attributeContainers) {
+ if (attributeContainer.isValid()) {
+ newFilter.add(nodeLevel.getValuePrecision().getCondition(contextState, attributeContainer.getAttribute(), attributeContainer.getValue()));
+ } else {
+ newFilter.add(ComparisonOperator.IS_NULL.create(contextState, attributeContainer.getAttribute(), null));
+ }
+ }
+ }
+ return newFilter;
+ }
/**
* Returns the SearchService
@@ -470,14 +568,17 @@
return Environment.class.getSimpleName().equals(entityType.getName());
}
+ private Attribute getFilterAttribute(NodeLevel nodeLevel) {
+ return nodeLevel.getFilterAttributes().stream().findFirst().orElseThrow(() -> new IllegalStateException("woops?!"));
+ }
+
+ private Attribute getLabelAttribute(NodeLevel nodeLevel) {
+ return nodeLevel.getLabelAttributes().stream().findFirst().orElseThrow(() -> new IllegalStateException("woops?!"));
+ }
+
/**
* Compares {@link Node}s be {@link Node#getLabel()}.
*/
- public static final Comparator<Node> compareByLabel = new Comparator<Node>() {
- @Override
- public int compare(Node o1, Node o2) {
- return o1.getLabel().compareTo(o2.getLabel());
- }
- };
+ public static final Comparator<Node> compareByLabel = Comparator.comparing(Node::getLabel);
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/MDMExpressionLanguageService.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/MDMExpressionLanguageService.java
new file mode 100644
index 0000000..d0b5fd5
--- /dev/null
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/MDMExpressionLanguageService.java
@@ -0,0 +1,93 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.control;
+
+import java.lang.reflect.Method;
+
+import javax.el.ELManager;
+import javax.el.ELProcessor;
+import javax.el.ExpressionFactory;
+import javax.el.StandardELContext;
+import javax.el.ValueExpression;
+
+import org.antlr.v4.tool.Attribute;
+import org.eclipse.mdm.nodeprovider.entity.NodeLevel;
+import org.eclipse.mdm.nodeprovider.entity.AttributeContainerListResovler;
+import org.eclipse.mdm.nodeprovider.utils.ExpressionLanguageMethodProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MDMExpressionLanguageService {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MDMExpressionLanguageService.class);
+
+ private ELProcessor processor;
+ private ExpressionFactory factory;
+
+ /**
+ * Intentionally empty
+ */
+ public MDMExpressionLanguageService() {
+ processor = new ELProcessor();
+ factory = ELManager.getExpressionFactory();
+ init();
+ }
+
+ /**
+ * Initialize functionMapper and resolvers for expression language interpretation.
+ */
+ public void init() {
+ ELManager manager = processor.getELManager();
+ manager.addELResolver(new AttributeContainerListResovler());
+
+ for(Method method : ExpressionLanguageMethodProvider.class.getMethods()) {
+ try {
+ processor.defineFunction("fn", "", method);
+ } catch (NoSuchMethodException e) {
+ LOG.warn("Could not add method: " + method.toString());
+ }
+ };
+ }
+
+ /**
+ * Parses {@link ValueExpression} of given {@link NodeLevel} if present.
+ *
+ * @param nodeLevel the node level
+ * @return the value expression, or null if none present
+ */
+ public ValueExpression parseValueExpression(String expressionString) {
+ StandardELContext elContext = processor.getELManager().getELContext();
+ ValueExpression valueExpression = null;
+ if (expressionString != null) {
+ valueExpression = factory.createValueExpression(elContext, expressionString, String.class);
+ }
+ return valueExpression;
+ }
+
+ /**
+ * Evaluates the {@link ValueExpression} with respect to given properties.
+ *
+ * @param valueExpression the value expression
+ * @param properties the properties to be inserted
+ * @return
+ */
+ public String evaluateValueExpression(ValueExpression valueExpression, Object ...properties) {
+ StandardELContext context = processor.getELManager().getELContext();
+ for (Object prop : properties) {
+ context.putContext(prop.getClass(), prop);
+ }
+ return valueExpression.getValue(context).toString();
+ }
+}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderException.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderException.java
index f182944..0a258ee 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderException.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderException.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.control;
/**
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderRepository.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderRepository.java
index 4fbd8ff..29ef1d8 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderRepository.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/control/NodeProviderRepository.java
@@ -24,7 +24,6 @@
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
-import org.eclipse.mdm.businessobjects.boundary.ChannelResource;
import org.eclipse.mdm.connector.boundary.ConnectorService;
import org.eclipse.mdm.nodeprovider.entity.NodeProvider;
import org.eclipse.mdm.nodeprovider.entity.NodeProviderRoot;
@@ -43,7 +42,7 @@
private static final long serialVersionUID = -3081666937067348673L;
- private static final Logger LOG = LoggerFactory.getLogger(ChannelResource.class);
+ private static final Logger LOG = LoggerFactory.getLogger(NodeProviderRepository.class);
private Map<String, NodeProvider> nodeProviders = new HashMap<>();
@@ -85,12 +84,14 @@
putNodeProvider("default", defaultNP);
LOG.trace("Registered default nodeprovider.");
+
+ MDMExpressionLanguageService expressionLanguageService = new MDMExpressionLanguageService();
List<PreferenceMessage> msgs = preferenceService.getPreferences("system", "nodeprovider.");
for (PreferenceMessage msg : msgs) {
try {
NodeProviderRoot root = parsePreference(msg.getValue());
- putNodeProvider(root.getId(), new GenericNodeProvider(connectorService, root));
+ putNodeProvider(root.getId(), new GenericNodeProvider(connectorService, root, expressionLanguageService));
LOG.trace("Registered generic nodeprovider '{}'.", root.getId());
} catch (RuntimeException e) {
e.printStackTrace();
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/AttributeContainerListResovler.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/AttributeContainerListResovler.java
new file mode 100644
index 0000000..bebe8c1
--- /dev/null
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/AttributeContainerListResovler.java
@@ -0,0 +1,76 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
+
+import java.beans.FeatureDescriptor;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Optional;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+import org.eclipse.mdm.nodeprovider.control.AttributeContainer;
+
+public class AttributeContainerListResovler extends ELResolver {
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ return true;
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ Optional<Object> resolved = getValue(context, property);
+ if (resolved.isPresent()) {
+ context.setPropertyResolved(base, property);
+ }
+ return resolved.orElseGet(null);
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ Optional<Object> resolved = getValue(context, property);
+ if (resolved.isPresent()) {
+ return resolved.get().getClass();
+ } else {
+ return null;
+ }
+ }
+
+ private Optional<Object> getValue(ELContext context, Object property) {
+ ArrayList<AttributeContainer> labelAttrContainers = (ArrayList<AttributeContainer>) context.getContext(ArrayList.class);
+ return labelAttrContainers.stream()
+ .filter(attr -> attr.getAttribute().getName().equalsIgnoreCase(property.toString()))
+ .map(AttributeContainer::getValue).findFirst();
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeLevel.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeLevel.java
index 9d5aca4..a407b22 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeLevel.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeLevel.java
@@ -1,9 +1,27 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
+import static java.util.stream.Collectors.toList;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import javax.el.ValueExpression;
+
import org.eclipse.mdm.api.base.adapter.Attribute;
import org.eclipse.mdm.api.base.adapter.EntityType;
import org.eclipse.mdm.api.base.search.ContextState;
@@ -25,8 +43,10 @@
private EntityType type;
- private Attribute idAttribute;
- private Attribute labelAttribute;
+ private List<Attribute> filterAttributes;
+ private List<Attribute> labelAttributes;
+ private ValueExpression labelExpression;
+ private ValuePrecision valuePrecision;
private List<SortAttribute> orderAttributes;
private ContextState contextState = null;
@@ -50,11 +70,37 @@
* label attributes.
*
* @param type
- * @param idAttribute
+ * @param filterAttribute
* @param labelAttribute
*/
- public NodeLevel(EntityType type, Attribute idAttribute, Attribute labelAttribute) {
- this(type, idAttribute, labelAttribute, Arrays.asList(new SortAttribute(labelAttribute)));
+ public NodeLevel(EntityType type, Attribute filterAttribute, Attribute labelAttribute) {
+ this(type, filterAttribute, labelAttribute, Arrays.asList(new SortAttribute(labelAttribute)));
+ }
+
+ /**
+ * Constructs NodeLevel for the given {@link EntityType} using the given ID and
+ * label attributes with the given list of order attributes.
+ *
+ * @param type
+ * @param filterAttribute
+ * @param labelAttribute
+ * @param orderAttributes
+ */
+ public NodeLevel(EntityType type, Attribute filterAttribute, Attribute labelAttribute,
+ List<SortAttribute> orderAttributes) {
+ this(type, Arrays.asList(filterAttribute), Arrays.asList(labelAttribute), orderAttributes);
+ }
+
+ /**
+ * Constructs NodeLevel for the given {@link EntityType} using the given ID and
+ * label attributes with the given list of order attributes.
+ *
+ * @param type
+ * @param filterAttributes
+ * @param labelAttributes
+ */
+ public NodeLevel(EntityType type, List<Attribute> filterAttributes, List<Attribute> labelAttributes) {
+ this(type, filterAttributes, labelAttributes, labelAttributes.stream().map(SortAttribute::new).collect(toList()));
}
/**
@@ -62,16 +108,17 @@
* label attributes with the given list of order attributes.
*
* @param type
- * @param idAttribute
+ * @param filterAttribute
* @param labelAttribute
* @param orderAttributes
*/
- public NodeLevel(EntityType type, Attribute idAttribute, Attribute labelAttribute,
- List<SortAttribute> orderAttributes) {
+ public NodeLevel(EntityType type, List<Attribute> filterAttribute, List<Attribute> labelAttribute,
+ List<SortAttribute> orderAttributes) {
this.type = type;
- this.idAttribute = idAttribute;
- this.labelAttribute = labelAttribute;
+ this.filterAttributes = filterAttribute;
+ this.labelAttributes = labelAttribute;
this.orderAttributes = Lists.newArrayList(orderAttributes);
+ this.valuePrecision = ValuePrecision.EXACT;
}
/**
@@ -119,15 +166,15 @@
/**
* @return the ID attribute
*/
- public Attribute getIdAttribute() {
- return idAttribute;
+ public List<Attribute> getFilterAttributes() {
+ return filterAttributes;
}
/**
* @return the label attribute
*/
- public Attribute getLabelAttribute() {
- return labelAttribute;
+ public List<Attribute> getLabelAttributes() {
+ return labelAttributes;
}
/**
@@ -158,12 +205,42 @@
public String toString() {
return MoreObjects.toStringHelper(NodeLevel.class)
.add("type", type)
- .add("idAttribute", idAttribute)
- .add("labelAttribute", labelAttribute)
+ .add("filterAttributes", filterAttributes)
+ .add("labelAttributes", labelAttributes)
+ .add("labelExpression", labelExpression)
.add("orderAttribute", orderAttributes)
.add("contextState", contextState)
.add("isVirtual", isVirtual)
+ .add("valuePrecision", valuePrecision)
.add("child", child)
.toString();
}
+
+ /**
+ * @return the labelExpression
+ */
+ public ValueExpression getLabelExpression() {
+ return labelExpression;
+ }
+
+ /**
+ * @param labelExpression the labelExpression to set
+ */
+ public void setLabelExpression(ValueExpression labelExpression) {
+ this.labelExpression = labelExpression;
+ }
+
+ /**
+ * @return the valuePrecision
+ */
+ public ValuePrecision getValuePrecision() {
+ return valuePrecision;
+ }
+
+ /**
+ * @param valuePrecision the valuePrecision to set
+ */
+ public void setValuePrecision(ValuePrecision valuePrecision) {
+ this.valuePrecision = valuePrecision != null ? valuePrecision : ValuePrecision.EXACT;
+ }
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProvider.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProvider.java
index b66da44..60c3274 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProvider.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProvider.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
import java.util.List;
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRoot.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRoot.java
index 3d89c19..9839a29 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRoot.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRoot.java
@@ -1,9 +1,24 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+import org.eclipse.mdm.api.base.adapter.Attribute;
import org.eclipse.mdm.api.base.model.Environment;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
@@ -143,13 +158,13 @@
/**
* @param context
* @param type
- * @param idAttribute
+ * @param filterAttribute
* @return NodeLevel for given type and idAttribute
*/
- public NodeLevel getNodeLevel(ApplicationContext context, String type, String idAttribute) {
+ public NodeLevel getNodeLevel(ApplicationContext context, String type, String filterAttribute) {
NodeLevel n = getNodeLevel(context.getSourceName());
- while (n != null && !nodeLevelMatches(n, type, idAttribute)) {
+ while (n != null && !nodeLevelMatches(n, type, filterAttribute)) {
n = n.getChild();
}
@@ -159,13 +174,29 @@
/**
* @param nodeLevel
* @param type
- * @param idAttribute
+ * @param filterAttribute
* @return true, if the given type and idAttribute matches the nodeLevel
*/
- private boolean nodeLevelMatches(NodeLevel nodeLevel, String type, String idAttribute) {
+ /**
+ * TODO 05.02.2021, jst:
+ * Match should respect labelAttributes / value precision to work properly for nested virtual nodes of same type
+ * with different value precision.
+ *
+ * Current fails in scenarios like:
+ *
+ * (...)
+ * |
+ * |---Test.DateCreated - YEAR
+ * |
+ * |---Test.DateCreated - MONTH
+ * |
+ * |--- Test.DateCreated - DAY
+ */
+ private boolean nodeLevelMatches(NodeLevel nodeLevel, String type, String filterAttribute) {
+ String first = nodeLevel.getFilterAttributes().stream().findFirst().map(Attribute::getName).orElse(null);
return ServiceUtils.workaroundForTypeMapping(type)
.equals(ServiceUtils.workaroundForTypeMapping(nodeLevel.getEntityType()))
- && (Strings.isNullOrEmpty(idAttribute) || idAttribute.equals(nodeLevel.getIdAttribute().getName()));
+ && (Strings.isNullOrEmpty(filterAttribute) || filterAttribute.equals(first));
}
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/Order.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/Order.java
index b85e2fa..b3c9cc7 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/Order.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/Order.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
/**
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/SortAttribute.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/SortAttribute.java
index 18abe11..0819a37 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/SortAttribute.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/SortAttribute.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
import org.eclipse.mdm.api.base.adapter.Attribute;
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/ValuePrecision.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/ValuePrecision.java
new file mode 100644
index 0000000..7daff9d
--- /dev/null
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/entity/ValuePrecision.java
@@ -0,0 +1,147 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.entity;
+
+import java.time.LocalDateTime;
+import java.time.temporal.TemporalAdjusters;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.ComparisonOperator;
+import org.eclipse.mdm.api.base.query.Condition;
+import org.eclipse.mdm.api.base.search.ContextState;
+
+public enum ValuePrecision {
+
+ EXACT {
+ @Override public void applyOn(Value value) {
+ // Intentionally empty
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return ComparisonOperator.EQUAL.create(contextState, attribute, value);
+ }
+ },
+ YEAR {
+ @Override public void applyOn(Value value) {
+ ValuePrecision.applyDate(value, YEAR);
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return getDateCondition(contextState, attribute, value, YEAR);
+ }
+ },
+ MONTH {
+ @Override public void applyOn(Value value) {
+ ValuePrecision.applyDate(value, MONTH);
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return getDateCondition(contextState, attribute, value, MONTH);
+ }
+ },
+ DAY {
+ @Override public void applyOn(Value value) {
+ ValuePrecision.applyDate(value, DAY);
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return getDateCondition(contextState, attribute, value, DAY);
+ }
+ },
+ HOUR {
+ @Override public void applyOn(Value value) {
+ ValuePrecision.applyDate(value, HOUR);
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return getDateCondition(contextState, attribute, value, HOUR);
+ }
+ },
+ MINUTE {
+ @Override public void applyOn(Value value) {
+ ValuePrecision.applyDate(value, MINUTE);
+ }
+ @Override public Condition getCondition(ContextState contextState, Attribute attribute, Object value) {
+ return getDateCondition(contextState, attribute, value, MINUTE);
+ }
+ };
+
+ /**
+ * Applies this {@link ValuePrecision} to given {@link Value}s value, if acceptable for related {@link ValueType}.
+ *
+ * @param value the value
+ */
+ abstract public void applyOn(Value value);
+ abstract public Condition getCondition(ContextState contextState, Attribute attribute, Object value);
+
+ /**
+ * Applies this {@link ValuePrecision} to given {@link Value}s value, for {@link ValueType.DATE}.
+ *
+ * @param value the value
+ */
+ private static void applyDate(Value value, ValuePrecision precision) {
+ if (value.getValueType() == ValueType.DATE) {
+ LocalDateTime dateTime = value.extract();
+ if (dateTime != null) {
+ switch (precision) {
+ case YEAR:
+ dateTime = dateTime.with(TemporalAdjusters.firstDayOfYear());
+ // no break intentionally;
+ case MONTH:
+ dateTime = dateTime.with(TemporalAdjusters.firstDayOfMonth());
+ // no break intentionally;
+ case DAY:
+ dateTime = dateTime.withHour(0);
+ // no break intentionally;
+ case HOUR:
+ dateTime = dateTime.withMinute(0);
+ // no break intentionally;
+ case MINUTE:
+ dateTime = dateTime.withSecond(0);
+ break;
+ default:
+ // Intentionally empty.
+ }
+ value.set(dateTime);
+ }
+ }
+ }
+
+ private static Condition getDateCondition(ContextState contextState, Attribute attribute, Object value, ValuePrecision precision) {
+ if (attribute.getValueType() == ValueType.DATE) {
+ LocalDateTime start = (LocalDateTime) value;
+ LocalDateTime end;
+ switch (precision) {
+ case YEAR:
+ end = start.plusYears(1);
+ break;
+ case MONTH:
+ end = start.plusMonths(1);
+ break;
+ case DAY:
+ end = start.plusDays(1);
+ break;
+ case HOUR:
+ end = start.plusHours(1);
+ break;
+ case MINUTE:
+ end = start.plusMinutes(1);
+ break;
+ default:
+ end = start.plusNanos(1);
+ }
+ return ComparisonOperator.BETWEEN.create(contextState, attribute, new LocalDateTime[] {start, end.minusNanos(1)});
+ } else {
+ return ComparisonOperator.EQUAL.create(contextState, attribute, value);
+ }
+ }
+}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/ExpressionLanguageMethodProvider.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/ExpressionLanguageMethodProvider.java
new file mode 100644
index 0000000..6674eb6
--- /dev/null
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/ExpressionLanguageMethodProvider.java
@@ -0,0 +1,31 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.utils;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * All functions in the {@link ExpressionLanguageMethodProvider} are reflected in the 'fn' namespace in the expression language context.
+ * Therefore, they have to be static.
+ *
+ */
+public class ExpressionLanguageMethodProvider {
+
+ public static String formatDate(LocalDateTime input, String format) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
+ return input.format(formatter);
+ }
+}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/SerializationUtil.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/SerializationUtil.java
index b877cae..f26e463 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/SerializationUtil.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/SerializationUtil.java
@@ -1,8 +1,24 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.utils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
import java.util.Base64;
+import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -16,6 +32,7 @@
import org.eclipse.mdm.businessobjects.control.FilterParser;
import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.nodeprovider.control.MDMExpressionLanguageService;
import org.eclipse.mdm.nodeprovider.control.NodeProviderException;
import org.eclipse.mdm.nodeprovider.entity.NodeLevel;
import org.eclipse.mdm.nodeprovider.entity.NodeProviderRoot;
@@ -110,8 +127,6 @@
* Serialize a {@link NodeLevel} and convert it to a {@link NodeLevelDTO} JSON
* string.
*
- * @param context
- * @param json
* @return NodeLevel
*/
public static String serializeNodeLevel(NodeLevel nl) {
@@ -226,8 +241,11 @@
public static NodeLevelDTO convert(NodeLevel nl) {
NodeLevelDTO nld = new NodeLevelDTO();
nld.setType(nl.getEntityType().getName());
- nld.setIdAttribute(nl.getIdAttribute().getName());
- nld.setLabelAttribute(nl.getLabelAttribute().getName());
+ nld.setFilterAttributes(Arrays.asList(getFilterAttribute(nl)));
+ nld.setLabelAttributes(Arrays.asList(getLabelAttribute(nl)));
+ if (nl.getLabelExpression() != null) {
+ nld.setLabelExpression(nl.getLabelExpression().getExpressionString());
+ }
nld.setContextState(nl.getContextState());
nld.setVirtual(nl.isVirtual());
nld.setOrderAttributes(nl.getOrderAttributes().stream()
@@ -235,6 +253,7 @@
if (nl.getChild() != null) {
nld.setChild(convert(nl.getChild()));
}
+ nld.setValuePrecision(nl.getValuePrecision());
return nld;
}
@@ -250,16 +269,34 @@
ModelManager mm = context.getModelManager().get();
EntityType e = mm.getEntityType(ServiceUtils.invertMapping(nld.getType()));
- NodeLevel nl = new NodeLevel(e, e.getAttribute(nld.getIdAttribute()), e.getAttribute(nld.getLabelAttribute()));
+ List<Attribute> filterAttributes = nld.getFilterAttributes().stream()
+ .map(e::getAttribute)
+ .collect(Collectors.toList());
+
+ List<Attribute> labelAttributes = nld.getLabelAttributes().stream()
+ .map(e::getAttribute)
+ .collect(Collectors.toList());
+
+ NodeLevel nl = new NodeLevel(e, filterAttributes, labelAttributes);
nl.setContextState(nld.getContextState());
nl.setVirtual(nld.isVirtual());
nl.getOrderAttributes().addAll(nld.getOrderAttributes().entrySet().stream()
.map(x -> new SortAttribute(e.getAttribute(x.getKey()), x.getValue())).collect(Collectors.toList()));
-
+ MDMExpressionLanguageService inst = new MDMExpressionLanguageService();
+ nl.setLabelExpression(inst.parseValueExpression(nld.getLabelExpression()));
+ nl.setValuePrecision(nld.getValuePrecision());
if (nld.getChild() != null) {
nl.setChild(convert(context, nld.getChild()));
}
return nl;
}
+
+ private static String getFilterAttribute(NodeLevel nodeLevel) {
+ return nodeLevel.getFilterAttributes().stream().map(Attribute::getName).findFirst().orElseThrow(() -> new IllegalStateException("woops?!"));
+ }
+
+ private static String getLabelAttribute(NodeLevel nodeLevel) {
+ return nodeLevel.getLabelAttributes().stream().map(Attribute::getName).findFirst().orElseThrow(() -> new IllegalStateException("woops?!"));
+ }
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeLevelDTO.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeLevelDTO.java
index 3843db8..4c03513 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeLevelDTO.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeLevelDTO.java
@@ -1,9 +1,25 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.utils.dto;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.mdm.api.base.search.ContextState;
+import org.eclipse.mdm.nodeprovider.entity.ValuePrecision;
import org.eclipse.mdm.nodeprovider.entity.NodeLevel;
import org.eclipse.mdm.nodeprovider.entity.Order;
@@ -18,8 +34,10 @@
public class NodeLevelDTO {
private String type;
- private String idAttribute;
- private String labelAttribute;
+ private List<String> filterAttributes;
+ private List<String> labelAttributes;
+ private String labelExpression;
+ private ValuePrecision valuePrecision;
private Map<String, Order> orderAttributes = new HashMap<>();
private ContextState contextState = null;
@@ -45,29 +63,29 @@
/**
* @return the idAttribute
*/
- public String getIdAttribute() {
- return idAttribute;
+ public List<String> getFilterAttributes() {
+ return filterAttributes;
}
/**
- * @param idAttribute the idAttribute to set
+ * @param filterAttributes the idAttribute to set
*/
- public void setIdAttribute(String idAttribute) {
- this.idAttribute = idAttribute;
+ public void setFilterAttributes(List<String> filterAttributes) {
+ this.filterAttributes = filterAttributes;
}
/**
* @return the labelAttribute
*/
- public String getLabelAttribute() {
- return labelAttribute;
+ public List<String> getLabelAttributes() {
+ return labelAttributes;
}
/**
- * @param labelAttribute the labelAttribute to set
+ * @param labelAttributes the labelAttribute to set
*/
- public void setLabelAttribute(String labelAttribute) {
- this.labelAttribute = labelAttribute;
+ public void setLabelAttributes(List<String> labelAttributes) {
+ this.labelAttributes = labelAttributes;
}
/**
@@ -125,4 +143,34 @@
public void setChild(NodeLevelDTO child) {
this.child = child;
}
+
+ /**
+ *
+ * @return
+ */
+ public String getLabelExpression() {
+ return labelExpression;
+ }
+
+ /**
+ *
+ * @param labelExpression
+ */
+ public void setLabelExpression(String labelExpression) {
+ this.labelExpression = labelExpression;
+ }
+
+ /**
+ * @return the valuePrecision
+ */
+ public ValuePrecision getValuePrecision() {
+ return valuePrecision;
+ }
+
+ /**
+ * @param valuePrecision the valuePrecision to set
+ */
+ public void setValuePrecision(ValuePrecision valuePrecision) {
+ this.valuePrecision = valuePrecision;
+ }
}
diff --git a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeProviderRootDTO.java b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeProviderRootDTO.java
index f75f1dd..1d23d2b 100644
--- a/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeProviderRootDTO.java
+++ b/nucleus/businessobjects/src/main/java/org/eclipse/mdm/nodeprovider/utils/dto/NodeProviderRootDTO.java
@@ -1,3 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2015-2020 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.nodeprovider.utils.dto;
import java.util.HashMap;
diff --git a/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProviderTest.java b/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProviderTest.java
index 6c50e63..a737579 100644
--- a/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProviderTest.java
+++ b/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/control/GenericNodeProviderTest.java
@@ -1,5 +1,6 @@
package org.eclipse.mdm.nodeprovider.control;
+import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.PARAM_NAMESERVICE;
import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.PARAM_PASSWORD;
@@ -109,8 +110,10 @@
ConnectorService connectorService = Mockito.mock(ConnectorService.class);
Mockito.when(connectorService.getContexts()).thenReturn(Arrays.asList(context));
Mockito.when(connectorService.getContextByName(any())).thenReturn(context);
+
+ MDMExpressionLanguageService elService = Mockito.mock(MDMExpressionLanguageService.class);
- return new GenericNodeProvider(connectorService, npr);
+ return new GenericNodeProvider(connectorService, npr, elService);
}
@Test
@@ -318,31 +321,31 @@
EntityType etEnv = modelManager.getEntityType(Environment.class);
NodeLevel env = new NodeLevel(etEnv);
- env.getOrderAttributes().add(new SortAttribute(env.getLabelAttribute()));
+ env.getOrderAttributes().addAll(env.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel project = new NodeLevel(modelManager.getEntityType(Project.class));
- project.getOrderAttributes().add(new SortAttribute(project.getLabelAttribute()));
+ project.getOrderAttributes().addAll(project.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
EntityType vehicle = modelManager.getEntityType("vehicle");
NodeLevel vehicleModel = new NodeLevel(vehicle, vehicle.getAttribute("model"), vehicle.getAttribute("model"));
vehicleModel.setVirtual(true);
vehicleModel.setContextState(contextState);
- vehicleModel.getOrderAttributes().add(new SortAttribute(vehicleModel.getLabelAttribute()));
+ vehicleModel.getOrderAttributes().addAll(vehicleModel.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel test = new NodeLevel(modelManager.getEntityType(org.eclipse.mdm.api.base.model.Test.class));
- test.getOrderAttributes().add(new SortAttribute(test.getLabelAttribute()));
+ test.getOrderAttributes().addAll(test.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel testStep = new NodeLevel(modelManager.getEntityType(TestStep.class));
- testStep.getOrderAttributes().add(new SortAttribute(testStep.getLabelAttribute()));
+ testStep.getOrderAttributes().addAll(testStep.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel measurement = new NodeLevel(modelManager.getEntityType(Measurement.class));
- measurement.getOrderAttributes().add(new SortAttribute(measurement.getLabelAttribute()));
+ measurement.getOrderAttributes().addAll(measurement.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel channelGroup = new NodeLevel(modelManager.getEntityType(ChannelGroup.class));
- channelGroup.getOrderAttributes().add(new SortAttribute(channelGroup.getLabelAttribute()));
+ channelGroup.getOrderAttributes().addAll(channelGroup.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
NodeLevel channel = new NodeLevel(modelManager.getEntityType(Channel.class));
- channel.getOrderAttributes().add(new SortAttribute(channel.getLabelAttribute()));
+ channel.getOrderAttributes().addAll(channel.getLabelAttributes().stream().map(SortAttribute::new).collect(toList()));
env.setChild(project);
project.setChild(vehicleModel);
diff --git a/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRootTest.java b/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRootTest.java
index b7ce35a..3aca80c 100644
--- a/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRootTest.java
+++ b/nucleus/businessobjects/src/test/java/org/eclipse/mdm/nodeprovider/entity/NodeProviderRootTest.java
@@ -6,6 +6,7 @@
import org.eclipse.mdm.api.atfxadapter.ATFXContextFactory;
import org.eclipse.mdm.api.base.ConnectionException;
+import org.eclipse.mdm.api.base.adapter.Attribute;
import org.eclipse.mdm.api.base.adapter.EntityType;
import org.eclipse.mdm.api.base.adapter.ModelManager;
import org.eclipse.mdm.api.base.model.Channel;
@@ -57,49 +58,49 @@
NodeLevel nl0 = np.getNodeLevel(context, "Environment", null);
assertThat(nl0.getEntityType().getName()).isEqualTo("Environment");
- assertThat(nl0.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl0.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl0.getContextState()).isNull();
assertThat(nl0.isVirtual()).isFalse();
NodeLevel nl1 = np.getNodeLevel(context, "Project", null);
assertThat(nl1.getEntityType().getName()).isEqualTo("Project");
- assertThat(nl1.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl1.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl1.getContextState()).isNull();
assertThat(nl1.isVirtual()).isFalse();
NodeLevel nl2 = np.getNodeLevel(context, "vehicle", "model");
assertThat(nl2.getEntityType().getName()).isEqualTo("vehicle");
- assertThat(nl2.getIdAttribute().getName()).isEqualTo("model");
+ assertThat(nl2.getFilterAttributes()).extracting(Attribute::getName).containsExactly("model");
assertThat(nl2.getContextState()).isEqualTo(ContextState.MEASURED);
assertThat(nl2.isVirtual()).isTrue();
NodeLevel nl3 = np.getNodeLevel(context, "Test", null);
assertThat(nl3.getEntityType().getName()).isEqualTo("Test");
- assertThat(nl3.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl3.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl3.getContextState()).isNull();
assertThat(nl3.isVirtual()).isFalse();
NodeLevel nl4 = np.getNodeLevel(context, "TestStep", null);
assertThat(nl4.getEntityType().getName()).isEqualTo("TestStep");
- assertThat(nl4.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl4.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl4.getContextState()).isNull();
assertThat(nl4.isVirtual()).isFalse();
NodeLevel nl5 = np.getNodeLevel(context, "Measurement", null);
assertThat(nl5.getEntityType().getName()).isEqualTo("MeaResult");
- assertThat(nl5.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl5.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl5.getContextState()).isNull();
assertThat(nl5.isVirtual()).isFalse();
NodeLevel nl6 = np.getNodeLevel(context, "ChannelGroup", null);
assertThat(nl6.getEntityType().getName()).isEqualTo("SubMatrix");
- assertThat(nl6.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl6.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl6.getContextState()).isNull();
assertThat(nl6.isVirtual()).isFalse();
NodeLevel nl7 = np.getNodeLevel(context, "Channel", null);
assertThat(nl7.getEntityType().getName()).isEqualTo("MeaQuantity");
- assertThat(nl7.getIdAttribute().getName()).isEqualTo("Id");
+ assertThat(nl7.getFilterAttributes()).extracting(Attribute::getName).containsExactly("Id");
assertThat(nl7.getContextState()).isNull();
assertThat(nl7.isVirtual()).isFalse();
}
diff --git a/nucleus/businessobjects/src/test/resources/nodeprovider_example.sql b/nucleus/businessobjects/src/test/resources/nodeprovider_example.sql
index 96bcadf..8f5cf64 100644
--- a/nucleus/businessobjects/src/test/resources/nodeprovider_example.sql
+++ b/nucleus/businessobjects/src/test/resources/nodeprovider_example.sql
@@ -5,40 +5,40 @@
"contexts" : {
"*" : {
"type" : "Environment",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"child" : {
"type" : "Project",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name", "Id"],
"child" : {
"type" : "vehicle",
- "idAttribute" : "model",
- "labelAttribute" : "model",
+ "filterAttributes" : ["model"],
+ "labelAttributes" : ["model", "Id"],
"orderAttributes" : {
"model" : "ASCENDING"
},
"contextState" : "MEASURED",
"child" : {
"type" : "Test",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"child" : {
"type" : "TestStep",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"child" : {
"type" : "Measurement",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"child" : {
"type" : "ChannelGroup",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"child" : {
"type" : "Channel",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false
},
"virtual" : false
diff --git a/nucleus/businessobjects/src/test/resources/nodeprovider_generic.json b/nucleus/businessobjects/src/test/resources/nodeprovider_generic.json
index 10f5025..7802538 100644
--- a/nucleus/businessobjects/src/test/resources/nodeprovider_generic.json
+++ b/nucleus/businessobjects/src/test/resources/nodeprovider_generic.json
@@ -4,44 +4,44 @@
"contexts" : {
"*" : {
"type" : "Environment",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false,
"child" : {
"type" : "Project",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name", "Id"],
"virtual" : false,
"child" : {
"type" : "vehicle",
- "idAttribute" : "model",
- "labelAttribute" : "model",
+ "filterAttributes" : ["model"],
+ "labelAttributes" : ["model"],
"virtual" : true,
"contextState": "MEASURED",
"child" : {
"type" : "Test",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false,
"child" : {
"type" : "TestStep",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false,
"child" : {
"type" : "Measurement",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false,
"child" : {
"type" : "ChannelGroup",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false,
"child" : {
"type" : "Channel",
- "idAttribute" : "Id",
- "labelAttribute" : "Name",
+ "filterAttributes" : ["Id"],
+ "labelAttributes" : ["Name"],
"virtual" : false
}
}
diff --git a/nucleus/preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java b/nucleus/preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
index 2e8d4a1..942238e 100644
--- a/nucleus/preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
+++ b/nucleus/preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
@@ -124,9 +124,8 @@
em.flush();
return convert(pe);
} else {
- throw new PreferenceException(
- "Only users with role " + ADMIN_ROLE
- + " are allowed to save Preferences outside of the USER scope!");
+ throw new PreferenceException("Only users with role " + ADMIN_ROLE
+ + " are allowed to save Preferences outside of the USER scope!");
}
}
@@ -145,9 +144,8 @@
}
private boolean isAllowed(Preference preference) {
- return sessionContext.isCallerInRole(ADMIN_ROLE)
- || (preference.getUser() != null
- && preference.getUser().equalsIgnoreCase(sessionContext.getCallerPrincipal().getName()));
+ return sessionContext.isCallerInRole(ADMIN_ROLE) || (preference.getUser() != null
+ && preference.getUser().equalsIgnoreCase(sessionContext.getCallerPrincipal().getName()));
}
private PreferenceMessage convert(Preference pe) {
diff --git a/nucleus/webclient/src/main/webapp/src/app/navigator/navigator.service.ts b/nucleus/webclient/src/main/webapp/src/app/navigator/navigator.service.ts
index c14fff3..84a6079 100644
--- a/nucleus/webclient/src/main/webapp/src/app/navigator/navigator.service.ts
+++ b/nucleus/webclient/src/main/webapp/src/app/navigator/navigator.service.ts
@@ -46,6 +46,7 @@
}
fireSelectedNodeChanged(node: Node) {
+ console.log(node)
this.selectedNodeChanged.next(node);
}