diff --git a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/JoinFunction.java b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/JoinFunction.java
index bb7a994137..fc7b83f0c9 100644
--- a/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/JoinFunction.java
+++ b/common/plugins/eu.esdihumboldt.hale.common.align/src/eu/esdihumboldt/hale/common/align/model/functions/JoinFunction.java
@@ -28,6 +28,12 @@ public interface JoinFunction {
*/
public static final String PARAMETER_JOIN = "join";
+ /**
+ * Name of the parameter that specifies if an inner join should be
+ * performed.
+ */
+ public static final String PARAMETER_INNER_JOIN = "innerJoin";
+
/**
* the join function Id
*/
diff --git a/common/plugins/eu.esdihumboldt.hale.common.instance/src/eu/esdihumboldt/hale/common/instance/model/impl/FilterResourceIteratorAdapter.java b/common/plugins/eu.esdihumboldt.hale.common.instance/src/eu/esdihumboldt/hale/common/instance/model/impl/FilterResourceIteratorAdapter.java
new file mode 100644
index 0000000000..25c3ae1ff2
--- /dev/null
+++ b/common/plugins/eu.esdihumboldt.hale.common.instance/src/eu/esdihumboldt/hale/common/instance/model/impl/FilterResourceIteratorAdapter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2024 wetransform GmbH
+ *
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution. If not, see .
+ *
+ * Contributors:
+ * wetransform GmbH
+ */
+
+package eu.esdihumboldt.hale.common.instance.model.impl;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import eu.esdihumboldt.hale.common.instance.model.ResourceIterator;
+
+/**
+ * {@link ResourceIterator} adapter for a normal iterator that can perform a
+ * conversion from the iterator elements to a target element type. It filters
+ * out items that are converted to a null
value.
+ *
+ * @param the source object type served by the wrapped iterator
+ * @param the object type served by the resource iterator
+ * @author Simon Templer
+ */
+public abstract class FilterResourceIteratorAdapter implements ResourceIterator {
+
+ /**
+ * The next matching instance
+ */
+ private T preview;
+
+ /**
+ * States if the value in {@link #preview} represents a valid element
+ */
+ private boolean previewPresent;
+
+ /**
+ * States if {@link #preview}/{@link #previewPresent} must be updated
+ */
+ private boolean updatePreview = true;
+
+ private final Iterator iterator;
+
+ /**
+ * Create a {@link ResourceIterator} adapter for the given iterator.
+ *
+ * @param iterator the iterator to adapt
+ */
+ public FilterResourceIteratorAdapter(Iterator iterator) {
+ super();
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ update(); // ensure previewPresent/preview are set
+
+ return previewPresent;
+ }
+
+ @Override
+ public T next() {
+ update(); // ensure previewPresent/preview are set
+
+ if (!previewPresent) {
+ throw new NoSuchElementException();
+ }
+
+ updatePreview = true; // next time, update the preview
+
+ return preview;
+ }
+
+ /**
+ * Move {@link #preview} to the next non-null converted item if possible,
+ * update {@link #previewPresent}.
+ */
+ private void update() {
+ if (updatePreview) {
+ previewPresent = false;
+
+ // find first instance matching the filter
+ while (!previewPresent && iterator.hasNext()) {
+ S item = iterator.next();
+ T converted = convert(item);
+
+ if (converted != null) {
+ previewPresent = true;
+ preview = converted;
+ }
+ }
+
+ if (!previewPresent) {
+ preview = null;
+ }
+
+ updatePreview = false;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "Removing instances not supported on filtered collections");
+ }
+
+ /**
+ * Convert an object before it is returned by {@link #next()}.
+ *
+ * @param next the object to convert
+ * @return the converted object or null if it should be skipped
+ */
+ protected abstract T convert(S next);
+
+ /**
+ * @see ResourceIterator#close()
+ */
+ @Override
+ public void close() {
+ if (iterator instanceof ResourceIterator>) {
+ ((ResourceIterator>) iterator).close();
+ }
+ }
+
+}
diff --git a/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinHandler.java b/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinHandler.java
index 079de93946..82fda96ad0 100644
--- a/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinHandler.java
+++ b/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinHandler.java
@@ -65,7 +65,7 @@ public ResourceIterator partitionInstances(InstanceCollection in
String transformationIdentifier, TransformationEngine engine,
ListMultimap transformationParameters,
Map executionParameters, TransformationLog log)
- throws TransformationException {
+ throws TransformationException {
if (transformationParameters == null
|| !transformationParameters.containsKey(PARAMETER_JOIN)
|| transformationParameters.get(PARAMETER_JOIN).isEmpty()) {
@@ -121,7 +121,13 @@ public ResourceIterator partitionInstances(InstanceCollection in
iterator.close();
}
+ boolean innerJoin = false; // default to false if not specified
+ List innerJoinValues = transformationParameters.get(PARAMETER_INNER_JOIN);
+ if (!innerJoinValues.isEmpty()) {
+ innerJoin = innerJoinValues.get(0).as(Boolean.class, innerJoin);
+ }
+
return new JoinIterator(instances, startInstances, joinDefinition.directParent, index,
- joinDefinition.joinTable, valueProcessor);
+ joinDefinition.joinTable, valueProcessor, innerJoin);
}
}
\ No newline at end of file
diff --git a/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinIterator.java b/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinIterator.java
index 4dc903831f..be15cb9a80 100644
--- a/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinIterator.java
+++ b/cst/plugins/eu.esdihumboldt.cst.functions.core/src/eu/esdihumboldt/cst/functions/core/join/JoinIterator.java
@@ -32,14 +32,14 @@
import eu.esdihumboldt.hale.common.instance.model.InstanceCollection;
import eu.esdihumboldt.hale.common.instance.model.InstanceReference;
import eu.esdihumboldt.hale.common.instance.model.ResolvableInstanceReference;
-import eu.esdihumboldt.hale.common.instance.model.impl.GenericResourceIteratorAdapter;
+import eu.esdihumboldt.hale.common.instance.model.impl.FilterResourceIteratorAdapter;
/**
* Iterator used by {@link JoinHandler}
*
* @author Florian Esser
*/
-class JoinIterator extends GenericResourceIteratorAdapter {
+class JoinIterator extends FilterResourceIteratorAdapter {
private final InstanceCollection instances;
// type -> direct-parent
@@ -51,35 +51,43 @@ class JoinIterator extends GenericResourceIteratorAdapter startInstances, int[] parent,
Map> index,
- Map> joinTable,
- ValueProcessor valueProcessor) {
+ Map> joinTable, ValueProcessor valueProcessor,
+ boolean innerJoin) {
super(startInstances.iterator());
this.instances = instances;
this.parent = parent;
this.index = index;
this.joinTable = joinTable;
this.valueProcessor = valueProcessor;
+ this.innerJoin = innerJoin;
+
}
- /**
- * @see eu.esdihumboldt.hale.common.instance.model.impl.GenericResourceIteratorAdapter#convert(java.lang.Object)
- */
@Override
protected FamilyInstance convert(InstanceReference next) {
FamilyInstance base = new FamilyInstanceImpl(instances.getInstance(next));
FamilyInstance[] currentInstances = new FamilyInstance[parent.length];
currentInstances[0] = base;
- join(currentInstances, 0);
+ if (!join(currentInstances, 0)) {
+ // skip this instance
+ return null;
+ }
return base;
}
-// Joins all direct children of the given type to currentInstances.
- private void join(FamilyInstance[] currentInstances, int currentType) {
+ /**
+ * Joins all direct children of the given type to currentInstances.
+ *
+ * @return if the instance should be skipped
+ */
+ private boolean join(FamilyInstance[] currentInstances, int currentType) {
// Join all types that are direct children of the last type.
for (int i = currentType + 1; i < parent.length; i++) {
if (parent[i] == currentType) {
@@ -140,12 +148,22 @@ private void join(FamilyInstance[] currentInstances, int currentType) {
}
parent.addChild(child);
currentInstances[i] = child;
- join(currentInstances, i);
+ if (!join(currentInstances, i)) {
+ return false;
+ }
}
currentInstances[i] = null;
}
+ else {
+ if (innerJoin) {
+ // no instances for this link
+ return false;
+ }
+ }
}
}
+
+ return true;
}
/**