Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FindObjects function #41

Merged
merged 3 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public abstract class BaseInterlisFunction implements InterlisFunction {
protected TransferDescription td;
protected Settings settings;
protected Validator validator;
protected ObjectPool objectPool;

@Override
public final void init(TransferDescription td, Settings settings, IoxValidationConfig validationConfig, ObjectPool objectPool, LogEventFactory logEventFactory) {
Expand All @@ -24,6 +25,7 @@ public final void init(TransferDescription td, Settings settings, IoxValidationC
this.td = td;
this.settings = settings;
this.validator = (Validator) settings.getTransientObject(IOX_VALIDATOR);
this.objectPool = objectPool;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package ch.geowerkstatt.ilivalidator.extensions.functions;

import ch.ehi.iox.objpool.impl.ObjPoolImpl2;
import ch.interlis.ili2c.metamodel.Element;
import ch.interlis.ili2c.metamodel.PathEl;
import ch.interlis.ili2c.metamodel.TextType;
import ch.interlis.ili2c.metamodel.Viewable;
import ch.interlis.iom.IomObject;
import ch.interlis.iox_j.validator.ObjectPoolKey;
import ch.interlis.iox_j.validator.Value;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public final class FindObjectsIoxPlugin extends BaseInterlisFunction {
private static final Map<FindObjectsKey, List<IomObject>> OBJECTS_CACHE = new HashMap<>();

@Override
public String getQualifiedIliName() {
return "GeoW_FunctionsExt.FindObjects";
}

@Override
protected Value evaluateInternal(String validationKind, String usageScope, IomObject contextObject, Value[] arguments) {
Value argClassName = arguments[0];
Value argPath = arguments[1];
Value argValue = arguments[2];

if (argClassName.isUndefined() || argPath.isUndefined()) {
return Value.createSkipEvaluation();
}

String className = argClassName.getValue();
String attributePath = argPath.getValue();
if (className == null || attributePath == null) {
return Value.createUndefined();
}

FindObjectsKey key = new FindObjectsKey(className, attributePath, argValue);
return new Value(OBJECTS_CACHE.computeIfAbsent(key, this::findObjects));
}

private List<IomObject> findObjects(FindObjectsKey key) {
Element classElement = td.getElement(key.className);
if (!(classElement instanceof Viewable)) {
throw new IllegalStateException("Could not find class \"" + key.className + "\"");
}
PathEl[] attributePath = EvaluationHelper.getAttributePathEl(validator, (Viewable) classElement, new Value(new TextType(), key.attributePath));

List<IomObject> objects = findObjectsOfClass(key.className);
return objects.stream()
.filter(object -> {
Value value = validator.getValueFromObjectPath(null, object, attributePath, null);
return value.compareTo(key.value) == 0;
})
.collect(Collectors.toList());
}

private List<IomObject> findObjectsOfClass(String className) {
List<IomObject> objects = new ArrayList<>();
for (String basketId : objectPool.getBasketIds()) {
ObjPoolImpl2<ObjectPoolKey, IomObject> basketObjectPool = objectPool.getObjectsOfBasketId(basketId);
Iterator<IomObject> valueIterator = basketObjectPool.valueIterator();
while (valueIterator.hasNext()) {
IomObject object = valueIterator.next();
if (object.getobjecttag().equals(className)) {
objects.add(object);
}
}
}
return objects;
}

private static final class FindObjectsKey {
private final String className;
private final String attributePath;
private final Value value;

FindObjectsKey(String className, String attributePath, Value value) {
this.className = className;
this.attributePath = attributePath;
this.value = value;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof FindObjectsKey)) {
return false;
}
FindObjectsKey that = (FindObjectsKey) o;
return className.equals(that.className)
&& attributePath.equals(that.attributePath)
&& value.compareTo(that.value) == 0;
}

@Override
public int hashCode() {
return Objects.hash(className, attributePath, value.getValue());
}
}
}
6 changes: 6 additions & 0 deletions src/model/GeoW_FunctionsExt.ili
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,10 @@ MODEL GeoW_FunctionsExt
!!@ fn.return = "TRUE, wenn sich zwei Linien überlappen oder zwischen zwei Linien eine gemeinsame Teilstrecke vorhanden ist";
!!@ fn.since = "2023-12-18";
FUNCTION PolylinesOverlap (Objects: OBJECTS OF ANYCLASS; LineAttr: TEXT): BOOLEAN;

!!@ fn.description = "Sucht im aktuellen Transfer nach Objekten der angegebenen Klasse, welche das Filterkriterium erfüllen. Für 'FilterAttr' soll der Pfad zum Attribut in INTERLIS 2 Syntax angegeben werden. Für 'FilterValue' kann ein beliebiger Wert angegeben werden.";
!!@ fn.param = "ClassName: Qualifizierter Klassenname (inklusive Modell und Topic) der Objekte, die gesucht werden. FilterAttr: Pfad zum Attribut, welches für den Filter verwendet werden soll. FilterValue: Wert für das Filterkriterium";
!!@ fn.return = "Alle Objekte der angegebenen Klasse aus dem aktuellen Transfer, welche das Filterkriterium erfüllen";
!!@ fn.since = "2024-01-10";
FUNCTION FindObjects(ClassName: TEXT; FilterAttr: TEXT; FilterValue: ANYSTRUCTURE): BAG OF ANYSTRUCTURE;
END GeoW_FunctionsExt.
26 changes: 26 additions & 0 deletions src/test/data/FindObjects/MandatoryConstraints.ili
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
INTERLIS 2.4;

MODEL TestSuite
AT "mailto:[email protected]" VERSION "2022-12-02" =
IMPORTS GeoW_FunctionsExt;

TOPIC FunctionTestTopic =

CLASS ReferencedClass =
textAttr: TEXT*16;
enumAttr: (val1,val2,val3);
numberAttr: 0..10;
END ReferencedClass;

CLASS BaseClass =
MANDATORY CONSTRAINT trueConstraintTextAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "textAttr", "Some Value")) == 2;
MANDATORY CONSTRAINT trueConstraintEnumAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "enumAttr", #val2)) == 3;
MANDATORY CONSTRAINT trueConstraintNumberAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "numberAttr", 3)) == 1;
MANDATORY CONSTRAINT falseConstraintTextAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "textAttr", "Some Value")) == 0;
domi-b marked this conversation as resolved.
Show resolved Hide resolved
MANDATORY CONSTRAINT falseConstraintEnumAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "enumAttr", #val2)) == 0;
MANDATORY CONSTRAINT falseConstraintNumberAttr: INTERLIS.elementCount(GeoW_FunctionsExt.FindObjects("TestSuite.FunctionTestTopic.ReferencedClass", "numberAttr", 3)) == 0;
END BaseClass;

END FunctionTestTopic;

END TestSuite.
42 changes: 42 additions & 0 deletions src/test/data/FindObjects/TestData.xtf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<ili:transfer xmlns:ili="http://www.interlis.ch/xtf/2.4/INTERLIS" xmlns:geom="http://www.interlis.ch/geometry/1.0"
xmlns:TestSuite="http://www.interlis.ch/xtf/2.4/TestSuite">
<ili:headersection>
<ili:models>
<ili:model>GeoW_FunctionsExt</ili:model>
<ili:model>TestSuite</ili:model>
</ili:models>
<ili:sender>ili2gpkg-4.6.1-63db90def1260a503f0f2d4cb846686cd4851184</ili:sender>
</ili:headersection>
<ili:datasection>
<TestSuite:FunctionTestTopic ili:bid="TestSuite.FunctionTestTopic">
<TestSuite:BaseClass ili:tid="base">
</TestSuite:BaseClass>
<TestSuite:ReferencedClass ili:tid="1">
<TestSuite:textAttr>Some Value</TestSuite:textAttr>
<TestSuite:enumAttr>val2</TestSuite:enumAttr>
<TestSuite:numberAttr>2</TestSuite:numberAttr>
</TestSuite:ReferencedClass>
<TestSuite:ReferencedClass ili:tid="2">
<TestSuite:textAttr>aaa</TestSuite:textAttr>
<TestSuite:enumAttr>val2</TestSuite:enumAttr>
<TestSuite:numberAttr>2</TestSuite:numberAttr>
</TestSuite:ReferencedClass>
<TestSuite:ReferencedClass ili:tid="3">
<TestSuite:textAttr>aaa</TestSuite:textAttr>
<TestSuite:enumAttr>val2</TestSuite:enumAttr>
<TestSuite:numberAttr>2</TestSuite:numberAttr>
</TestSuite:ReferencedClass>
<TestSuite:ReferencedClass ili:tid="4">
<TestSuite:textAttr>Some Value</TestSuite:textAttr>
<TestSuite:enumAttr>val3</TestSuite:enumAttr>
<TestSuite:numberAttr>1</TestSuite:numberAttr>
</TestSuite:ReferencedClass>
<TestSuite:ReferencedClass ili:tid="5">
<TestSuite:textAttr>bbb</TestSuite:textAttr>
<TestSuite:enumAttr>val1</TestSuite:enumAttr>
<TestSuite:numberAttr>3</TestSuite:numberAttr>
</TestSuite:ReferencedClass>
</TestSuite:FunctionTestTopic>
</ili:datasection>
</ili:transfer>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ch.geowerkstatt.ilivalidator.extensions.functions;

import ch.interlis.ili2c.Ili2cFailure;
import ch.interlis.iox.IoxException;
import com.vividsolutions.jts.util.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class FindObjectsIoxPluginTest {
protected static final String TEST_DATA = "FindObjects/TestData.xtf";
private ValidationTestHelper vh = null;

@BeforeEach
void setUp() {
vh = new ValidationTestHelper();
vh.addFunction(new FindObjectsIoxPlugin());
}

@Test
void mandatoryConstraint() throws Ili2cFailure, IoxException {
vh.runValidation(new String[]{TEST_DATA}, new String[]{"FindObjects/MandatoryConstraints.ili"});
Assert.equals(3, vh.getErrs().size());
AssertionHelper.assertNoConstraintError(vh, "trueConstraintTextAttr");
AssertionHelper.assertNoConstraintError(vh, "trueConstraintEnumAttr");
AssertionHelper.assertNoConstraintError(vh, "trueConstraintNumberAttr");
AssertionHelper.assertConstraintErrors(vh, 1, "base", "falseConstraintTextAttr");
AssertionHelper.assertConstraintErrors(vh, 1, "base", "falseConstraintEnumAttr");
AssertionHelper.assertConstraintErrors(vh, 1, "base", "falseConstraintNumberAttr");
}
}
Loading