This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new f7ccb8ae55 Update info streams correctly across multiple transforms,
fixes #4877 (#6591)
f7ccb8ae55 is described below
commit f7ccb8ae55490649e84c52aa8cbf84ac8147e540
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Feb 17 13:51:16 2026 +0100
Update info streams correctly across multiple transforms, fixes #4877
(#6591)
* Update info streams correctly across multiple transforms, fixes #4877
* spotless
---
assemblies/debug/pom.xml | 30 +++++
.../pipeline/transforms/append/AppendDialog.java | 18 ++-
.../hop/pipeline/transforms/append/AppendMeta.java | 114 +++++++++++++++-
.../transforms/joinrows/JoinRowsDialog.java | 3 +
.../pipeline/transforms/joinrows/JoinRowsMeta.java | 93 ++++++++++++-
.../joinrows/messages/messages_en_US.properties | 1 +
.../transforms/mergejoin/MergeJoinDialog.java | 12 ++
.../transforms/mergejoin/MergeJoinMeta.java | 117 ++++++++++++++--
.../multimerge/MultiMergeJoinDialog.java | 14 ++
.../transforms/multimerge/MultiMergeJoinMeta.java | 148 +++++++++++++++++++--
.../hopgui/file/pipeline/HopGuiPipelineGraph.java | 38 +++++-
.../delegates/HopGuiPipelineTransformDelegate.java | 11 +-
12 files changed, 569 insertions(+), 30 deletions(-)
diff --git a/assemblies/debug/pom.xml b/assemblies/debug/pom.xml
index 2fea07e0f7..857efa8161 100644
--- a/assemblies/debug/pom.xml
+++ b/assemblies/debug/pom.xml
@@ -427,6 +427,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-transform-append</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.hop</groupId>
<artifactId>hop-transform-blockuntiltransformsfinish</artifactId>
@@ -469,6 +475,24 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-transform-joinrows</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-transform-mergejoin</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-transform-multimerge</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.hop</groupId>
<artifactId>hop-transform-pipelineexecutor</artifactId>
@@ -499,6 +523,12 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-transform-streamlookup</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.hop</groupId>
<artifactId>hop-transform-textfile</artifactId>
diff --git
a/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendDialog.java
b/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendDialog.java
index cc0fc39503..3090704998 100644
---
a/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendDialog.java
+++
b/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendDialog.java
@@ -17,11 +17,13 @@
package org.apache.hop.pipeline.transforms.append;
+import java.util.List;
import org.apache.hop.core.Const;
import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.stream.IStream;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
@@ -160,9 +162,21 @@ public class AppendDialog extends BaseTransformDialog {
/** Copy information from the meta-data input to the dialog fields. */
public void getData() {
+ // If both fields are empty and exactly 2 transforms are attached,
auto-fill head and tail
+ if (Utils.isEmpty(input.getHeadTransformName())
+ && Utils.isEmpty(input.getTailTransformName())) {
+ String[] prev = pipelineMeta.getPrevTransformNames(transformName);
+ if (prev != null && prev.length == 2) {
+ input.setHeadTransformName(prev[0]);
+ input.setTailTransformName(prev[1]);
+ }
+ }
+ // Sync from hops (rename, insert-in-the-middle) and resolve streams
+ input.searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
- wHeadHop.setText(Const.NVL(input.getHeadTransformName(), ""));
- wTailHop.setText(Const.NVL(input.getTailTransformName(), ""));
+ List<IStream> infoStreams = input.getTransformIOMeta().getInfoStreams();
+ wHeadHop.setText(Const.NVL(infoStreams.get(0).getTransformName(), ""));
+ wTailHop.setText(Const.NVL(infoStreams.get(1).getTransformName(), ""));
wTransformName.selectAll();
wTransformName.setFocus();
diff --git
a/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendMeta.java
b/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendMeta.java
index caa00ee9c7..0b04495328 100644
---
a/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendMeta.java
+++
b/plugins/transforms/append/src/main/java/org/apache/hop/pipeline/transforms/append/AppendMeta.java
@@ -18,11 +18,13 @@
package org.apache.hop.pipeline.transforms.append;
import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.annotations.Transform;
import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.metadata.api.HopMetadataProperty;
@@ -75,9 +77,115 @@ public class AppendMeta extends BaseTransformMeta<Append,
AppendData> {
@Override
public void searchInfoAndTargetTransforms(List<TransformMeta> transforms) {
- List<IStream> streams = getTransformIOMeta().getInfoStreams();
- streams.get(0).setTransformMeta(TransformMeta.findTransform(transforms,
headTransformName));
- streams.get(1).setTransformMeta(TransformMeta.findTransform(transforms,
tailTransformName));
+ List<IStream> infoStreams = getTransformIOMeta().getInfoStreams();
+ if (infoStreams.size() < 2) {
+ return;
+ }
+ IStream headStream = infoStreams.get(0);
+ IStream tailStream = infoStreams.get(1);
+ // Respect user choice: only re-fill from stream/prev when they had set a
value (e.g. rename).
+ boolean hadHeadFilledIn = !Utils.isEmpty(headTransformName);
+ boolean hadTailFilledIn = !Utils.isEmpty(tailTransformName);
+
+ if (parentTransformMeta != null) {
+ PipelineMeta pipelineMeta = parentTransformMeta.getParentPipelineMeta();
+ if (pipelineMeta != null) {
+ String[] prev =
pipelineMeta.getPrevTransformNames(parentTransformMeta);
+ // Only auto-fill when both are empty (initial connect). Do not
re-fill when user cleared
+ // one.
+ if (prev != null && prev.length == 2) {
+ if (Utils.isEmpty(headTransformName) &&
Utils.isEmpty(tailTransformName)) {
+ if (headStream.getTransformMeta() != null &&
tailStream.getTransformMeta() != null) {
+ headTransformName = headStream.getTransformName();
+ tailTransformName = tailStream.getTransformName();
+ } else {
+ headTransformName = prev[0];
+ tailTransformName = prev[1];
+ setChanged();
+ }
+ }
+ }
+ // Clear names that no longer exist in prev (e.g. transform was
removed)
+ if (headTransformName != null && !ArrayUtils.contains(prev,
headTransformName)) {
+ headTransformName = null;
+ setChanged();
+ }
+ if (tailTransformName != null && !ArrayUtils.contains(prev,
tailTransformName)) {
+ tailTransformName = null;
+ setChanged();
+ }
+ }
+ }
+
+ // Resolve by name. Prefer stream only when the stored name is "stale":
empty (auto-fill),
+ // not in prev (insert-in-the-middle), or transform not found (rename).
+ String[] prev = null;
+ if (parentTransformMeta != null &&
parentTransformMeta.getParentPipelineMeta() != null) {
+ prev =
parentTransformMeta.getParentPipelineMeta().getPrevTransformNames(parentTransformMeta);
+ }
+ TransformMeta tmHead = null;
+ boolean headNameStale =
+ Utils.isEmpty(headTransformName)
+ || (prev != null && !ArrayUtils.contains(prev, headTransformName))
+ || TransformMeta.findTransform(transforms, headTransformName) ==
null;
+ boolean preferHeadStream =
+ headStream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, headStream.getTransformName())
+ && headNameStale
+ && hadHeadFilledIn;
+ if (preferHeadStream) {
+ headTransformName = headStream.getTransformName();
+ tmHead = headStream.getTransformMeta();
+ setChanged();
+ }
+ if (tmHead == null) {
+ tmHead = TransformMeta.findTransform(transforms, headTransformName);
+ if (tmHead == null
+ && headStream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, headStream.getTransformName())
+ && hadHeadFilledIn) {
+ headTransformName = headStream.getTransformName();
+ tmHead = TransformMeta.findTransform(transforms, headTransformName);
+ }
+ }
+ headStream.setTransformMeta(tmHead);
+ if (tmHead != null) {
+ headStream.setSubject(tmHead.getName());
+ }
+
+ TransformMeta tmTail = null;
+ boolean tailNameStale =
+ Utils.isEmpty(tailTransformName)
+ || (prev != null && !ArrayUtils.contains(prev, tailTransformName))
+ || TransformMeta.findTransform(transforms, tailTransformName) ==
null;
+ boolean preferTailStream =
+ tailStream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, tailStream.getTransformName())
+ && tailNameStale
+ && hadTailFilledIn;
+ if (preferTailStream) {
+ tailTransformName = tailStream.getTransformName();
+ tmTail = tailStream.getTransformMeta();
+ setChanged();
+ }
+ if (tmTail == null) {
+ tmTail = TransformMeta.findTransform(transforms, tailTransformName);
+ if (tmTail == null
+ && tailStream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, tailStream.getTransformName())
+ && hadTailFilledIn) {
+ tailTransformName = tailStream.getTransformName();
+ tmTail = TransformMeta.findTransform(transforms, tailTransformName);
+ }
+ }
+ tailStream.setTransformMeta(tmTail);
+ if (tmTail != null) {
+ tailStream.setSubject(tmTail.getName());
+ }
}
@Override
diff --git
a/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsDialog.java
b/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsDialog.java
index 9832eb816c..6cc4266eaf 100644
---
a/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsDialog.java
+++
b/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsDialog.java
@@ -259,6 +259,9 @@ public class JoinRowsDialog extends BaseTransformDialog {
/** Copy information from the meta-data input to the dialog fields. */
public void getData() {
+ // Sync from hops (rename, insert-in-the-middle) so main transform is up
to date
+ input.searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+
if (input.getPrefix() != null) {
wPrefix.setText(input.getPrefix());
}
diff --git
a/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsMeta.java
b/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsMeta.java
index da075f8d01..08e6266860 100644
---
a/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsMeta.java
+++
b/plugins/transforms/joinrows/src/main/java/org/apache/hop/pipeline/transforms/joinrows/JoinRowsMeta.java
@@ -21,6 +21,7 @@ import java.io.File;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.Condition;
import org.apache.hop.core.ICheckResult;
@@ -28,6 +29,7 @@ import org.apache.hop.core.annotations.Transform;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.metadata.api.HopMetadataProperty;
@@ -35,7 +37,13 @@ import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.metadata.api.IStringObjectConverter;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransformMeta;
+import org.apache.hop.pipeline.transform.ITransformIOMeta;
+import org.apache.hop.pipeline.transform.TransformIOMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
+import org.apache.hop.pipeline.transform.stream.IStream;
+import org.apache.hop.pipeline.transform.stream.IStream.StreamType;
+import org.apache.hop.pipeline.transform.stream.Stream;
+import org.apache.hop.pipeline.transform.stream.StreamIcon;
@Transform(
id = "JoinRows",
@@ -216,12 +224,95 @@ public class JoinRowsMeta extends
BaseTransformMeta<JoinRows, JoinRowsData> {
return null;
}
+ /**
+ * Returns the I/O meta with one INFO stream when "Main transform to read
from" is set (like
+ * Stream Lookup).
+ */
+ @Override
+ public ITransformIOMeta getTransformIOMeta() {
+ ITransformIOMeta ioMeta = super.getTransformIOMeta(false);
+ if (ioMeta == null) {
+ ioMeta = new TransformIOMeta(true, true, false, false, false, false);
+ IStream stream =
+ new Stream(
+ StreamType.INFO,
+ null,
+ BaseMessages.getString(PKG,
"JoinRowsMeta.InfoStream.Description"),
+ StreamIcon.INFO,
+ mainTransformName);
+ ioMeta.addStream(stream);
+ setTransformIOMeta(ioMeta);
+ }
+ return ioMeta;
+ }
+
+ @Override
+ public void resetTransformIoMeta() {
+ // Don't reset
+ }
+
/**
* @param transforms optionally search the info transform in a list of
transforms
*/
@Override
public void searchInfoAndTargetTransforms(List<TransformMeta> transforms) {
- mainTransform = TransformMeta.findTransform(transforms, mainTransformName);
+ List<IStream> infoStreams = getTransformIOMeta().getInfoStreams();
+ if (infoStreams.isEmpty()) {
+ mainTransform = TransformMeta.findTransform(transforms,
mainTransformName);
+ return;
+ }
+ IStream stream = infoStreams.get(0);
+
+ String[] prev = null;
+ if (parentTransformMeta != null &&
parentTransformMeta.getParentPipelineMeta() != null) {
+ prev =
parentTransformMeta.getParentPipelineMeta().getPrevTransformNames(parentTransformMeta);
+ }
+
+ // Clear name when no longer in prev (transform removed)
+ if (prev != null
+ && mainTransformName != null
+ && !ArrayUtils.contains(prev, mainTransformName)
+ && (stream.getTransformMeta() == null
+ || !ArrayUtils.contains(prev, stream.getTransformName()))) {
+ mainTransformName = null;
+ setChanged();
+ }
+
+ // Resolve: prefer stream when name is stale (rename /
insert-in-the-middle / detach).
+ // Do not re-fill from stream when mainTransformName is empty (user chose
to clear / no main).
+ boolean nameStale =
+ Utils.isEmpty(mainTransformName)
+ || (prev != null && !ArrayUtils.contains(prev, mainTransformName))
+ || TransformMeta.findTransform(transforms, mainTransformName) ==
null;
+ boolean preferStream =
+ stream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, stream.getTransformName())
+ && nameStale
+ && !Utils.isEmpty(mainTransformName);
+
+ TransformMeta tm = null;
+ if (preferStream) {
+ mainTransformName = stream.getTransformName();
+ mainTransform = stream.getTransformMeta();
+ tm = mainTransform;
+ setChanged();
+ }
+ if (tm == null) {
+ tm = TransformMeta.findTransform(transforms, mainTransformName);
+ if (tm == null && !Utils.isEmpty(mainTransformName) &&
stream.getTransformMeta() != null) {
+ mainTransformName = stream.getTransformName();
+ tm = TransformMeta.findTransform(transforms, mainTransformName);
+ setChanged();
+ }
+ mainTransform = tm;
+ }
+ stream.setTransformMeta(tm);
+ if (tm != null) {
+ stream.setSubject(tm.getName());
+ } else {
+ stream.setSubject(null);
+ }
}
@Override
diff --git
a/plugins/transforms/joinrows/src/main/resources/org/apache/hop/pipeline/transforms/joinrows/messages/messages_en_US.properties
b/plugins/transforms/joinrows/src/main/resources/org/apache/hop/pipeline/transforms/joinrows/messages/messages_en_US.properties
index 09975c0af7..17e3343c02 100644
---
a/plugins/transforms/joinrows/src/main/resources/org/apache/hop/pipeline/transforms/joinrows/messages/messages_en_US.properties
+++
b/plugins/transforms/joinrows/src/main/resources/org/apache/hop/pipeline/transforms/joinrows/messages/messages_en_US.properties
@@ -45,6 +45,7 @@ JoinRowsDialog.Temp.Label=temp
JoinRowsDialog.TempDir.Label=Temp directory
JoinRowsDialog.TempFilePrefix.Label=TMP-file prefix
JoinRowsDialog.TransformName.Label=Transform name
+JoinRowsMeta.InfoStream.Description=Main transform to read from
JoinRowsMeta.CheckResult.CouldNotFindFieldsFromPreviousTransforms=Couldn''t
find fields from previous transforms, check the hops...\!
JoinRowsMeta.CheckResult.DirectoryDoesNotExist=Directory [{0}] doesn''t exist!
JoinRowsMeta.CheckResult.DirectoryExists=] exists and is a directory
diff --git
a/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinDialog.java
b/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinDialog.java
index 3bd8f22ad4..1d024a77a0 100644
---
a/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinDialog.java
+++
b/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinDialog.java
@@ -297,6 +297,18 @@ public class MergeJoinDialog extends BaseTransformDialog {
/** Copy information from the meta-data input to the dialog fields. */
public void getData() {
+ // If both fields are empty and exactly 2 transforms are attached,
auto-fill first and second
+ if (Utils.isEmpty(input.getLeftTransformName())
+ && Utils.isEmpty(input.getRightTransformName())) {
+ String[] prev = pipelineMeta.getPrevTransformNames(transformName);
+ if (prev != null && prev.length == 2) {
+ input.setLeftTransformName(prev[0]);
+ input.setRightTransformName(prev[1]);
+ }
+ }
+ // Sync from hops (rename, insert-in-the-middle) and resolve streams
+ input.searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+
List<IStream> infoStreams = input.getTransformIOMeta().getInfoStreams();
wTransform1.setText(Const.NVL(infoStreams.get(0).getTransformName(), ""));
diff --git
a/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinMeta.java
b/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinMeta.java
index 763afc92d0..709d321157 100644
---
a/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinMeta.java
+++
b/plugins/transforms/mergejoin/src/main/java/org/apache/hop/pipeline/transforms/mergejoin/MergeJoinMeta.java
@@ -121,22 +121,119 @@ public class MergeJoinMeta extends
BaseTransformMeta<MergeJoin, MergeJoinData> {
@Override
public void searchInfoAndTargetTransforms(List<TransformMeta> transforms) {
List<IStream> infoStreams = getTransformIOMeta().getInfoStreams();
+ if (infoStreams.size() < 2) {
+ return;
+ }
+ IStream stream0 = infoStreams.get(0);
+ IStream stream1 = infoStreams.get(1);
+ // Respect user choice: only re-fill from stream/prev when they had set a
value (e.g. rename).
+ boolean hadLeftFilledIn = !Utils.isEmpty(leftTransformName);
+ boolean hadRightFilledIn = !Utils.isEmpty(rightTransformName);
+
if (parentTransformMeta != null) {
- String[] prev =
-
parentTransformMeta.getParentPipelineMeta().getPrevTransformNames(parentTransformMeta);
- if (leftTransformName != null && !ArrayUtils.contains(prev,
leftTransformName)) {
- leftTransformName = null;
+ PipelineMeta pipelineMeta = parentTransformMeta.getParentPipelineMeta();
+ if (pipelineMeta != null) {
+ String[] prev =
pipelineMeta.getPrevTransformNames(parentTransformMeta);
+ // Only auto-fill when both are empty (initial connect). Do not
re-fill when user cleared
+ // one.
+ if (prev != null && prev.length == 2) {
+ if (Utils.isEmpty(leftTransformName) &&
Utils.isEmpty(rightTransformName)) {
+ if (stream0.getTransformMeta() != null &&
stream1.getTransformMeta() != null) {
+ leftTransformName = stream0.getTransformName();
+ rightTransformName = stream1.getTransformName();
+ } else {
+ leftTransformName = prev[0];
+ rightTransformName = prev[1];
+ setChanged();
+ }
+ }
+ }
+ // Clear names that no longer exist in prev (e.g. transform was
removed / detached)
+ if (leftTransformName != null && !ArrayUtils.contains(prev,
leftTransformName)) {
+ leftTransformName = null;
+ setChanged();
+ }
+ if (rightTransformName != null && !ArrayUtils.contains(prev,
rightTransformName)) {
+ rightTransformName = null;
+ setChanged();
+ }
+ }
+ }
+
+ // Resolve by name. Prefer stream only when the stored name is "stale":
empty (auto-fill),
+ // not in prev (insert-in-the-middle), or transform not found (rename). Do
not prefer when
+ // the user has set a valid name (e.g. swapped order) so we don't
overwrite their choice.
+ String[] prev = null;
+ if (parentTransformMeta != null &&
parentTransformMeta.getParentPipelineMeta() != null) {
+ prev =
parentTransformMeta.getParentPipelineMeta().getPrevTransformNames(parentTransformMeta);
+ }
+ TransformMeta tm0 = null;
+ boolean name0Stale =
+ Utils.isEmpty(leftTransformName)
+ || (prev != null && !ArrayUtils.contains(prev, leftTransformName))
+ || TransformMeta.findTransform(transforms, leftTransformName) ==
null;
+ boolean preferStream0 =
+ stream0.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, stream0.getTransformName())
+ && name0Stale
+ && hadLeftFilledIn;
+ if (preferStream0) {
+ leftTransformName = stream0.getTransformName();
+ tm0 = stream0.getTransformMeta();
+ setChanged();
+ }
+ if (tm0 == null) {
+ tm0 = TransformMeta.findTransform(transforms, leftTransformName);
+ // Only use stream as fallback when stream's transform is still in prev
(e.g. rename). Do not
+ // re-apply stream when it points to a detached/removed transform.
+ if (tm0 == null
+ && stream0.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, stream0.getTransformName())
+ && hadLeftFilledIn) {
+ leftTransformName = stream0.getTransformName();
+ tm0 = TransformMeta.findTransform(transforms, leftTransformName);
setChanged();
}
- if (rightTransformName != null && !ArrayUtils.contains(prev,
rightTransformName)) {
- rightTransformName = null;
+ }
+ stream0.setTransformMeta(tm0);
+ if (tm0 != null) {
+ stream0.setSubject(tm0.getName());
+ }
+
+ TransformMeta tm1 = null;
+ boolean name1Stale =
+ Utils.isEmpty(rightTransformName)
+ || (prev != null && !ArrayUtils.contains(prev, rightTransformName))
+ || TransformMeta.findTransform(transforms, rightTransformName) ==
null;
+ boolean preferStream1 =
+ stream1.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, stream1.getTransformName())
+ && name1Stale
+ && hadRightFilledIn;
+ if (preferStream1) {
+ rightTransformName = stream1.getTransformName();
+ tm1 = stream1.getTransformMeta();
+ setChanged();
+ }
+ if (tm1 == null) {
+ tm1 = TransformMeta.findTransform(transforms, rightTransformName);
+ if (tm1 == null
+ && stream1.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, stream1.getTransformName())
+ && hadRightFilledIn) {
+ rightTransformName = stream1.getTransformName();
+ tm1 = TransformMeta.findTransform(transforms, rightTransformName);
setChanged();
}
}
-
infoStreams.get(0).setTransformMeta(TransformMeta.findTransform(transforms,
leftTransformName));
- infoStreams
- .get(1)
- .setTransformMeta(TransformMeta.findTransform(transforms,
rightTransformName));
+ stream1.setTransformMeta(tm1);
+ if (tm1 != null) {
+ stream1.setSubject(tm1.getName());
+ }
}
@Override
diff --git
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinDialog.java
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinDialog.java
index 45583b643e..8e539336a9 100644
---
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinDialog.java
+++
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinDialog.java
@@ -412,6 +412,20 @@ public class MultiMergeJoinDialog extends
BaseTransformDialog {
/** Copy information from the meta-data input to the dialog fields. */
public void getData() {
+ // If no inputs configured and at least 2 transforms are attached,
auto-fill from prev
+ if (joinMeta.getInputTransforms() == null ||
joinMeta.getInputTransforms().isEmpty()) {
+ String[] prev = pipelineMeta.getPrevTransformNames(transformName);
+ if (prev != null && prev.length >= 2) {
+ List<String> list = new ArrayList<>();
+ for (String p : prev) {
+ list.add(p);
+ }
+ joinMeta.setInputTransforms(list);
+ }
+ }
+ // Sync from hops (rename, insert-in-the-middle) and resolve streams
+ joinMeta.searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+
List<String> inputTransformNames = joinMeta.getInputTransforms();
if (inputTransformNames != null) {
String inputTransformName;
diff --git
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMeta.java
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMeta.java
index 5da674d066..3fcfd1428a 100644
---
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMeta.java
+++
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMeta.java
@@ -21,12 +21,14 @@ import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.annotations.Transform;
import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.i18n.BaseMessages;
@@ -35,7 +37,9 @@ import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.BaseTransformMeta;
import org.apache.hop.pipeline.transform.ITransformIOMeta;
+import org.apache.hop.pipeline.transform.TransformIOMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
+import org.apache.hop.pipeline.transform.stream.IStream;
import org.apache.hop.pipeline.transform.stream.IStream.StreamType;
import org.apache.hop.pipeline.transform.stream.Stream;
import org.apache.hop.pipeline.transform.stream.StreamIcon;
@@ -126,19 +130,145 @@ public class MultiMergeJoinMeta extends
BaseTransformMeta<MultiMergeJoin, MultiM
joinType = joinTypes[0];
}
+ /**
+ * Returns the I/O meta with INFO streams so the pipeline marks input hops
as info streams (like
+ * Merge Join / Append).
+ */
+ @Override
+ public ITransformIOMeta getTransformIOMeta() {
+ ITransformIOMeta ioMeta = super.getTransformIOMeta(false);
+ if (ioMeta == null) {
+ ioMeta = new TransformIOMeta(true, true, false, false, false, false);
+ int n = (inputTransforms != null && !inputTransforms.isEmpty()) ?
inputTransforms.size() : 2;
+ for (int i = 0; i < n; i++) {
+ ioMeta.addStream(
+ new Stream(
+ StreamType.INFO,
+ null,
+ BaseMessages.getString(PKG,
"MultiMergeJoin.InfoStream.Description"),
+ StreamIcon.INFO,
+ null));
+ }
+ setTransformIOMeta(ioMeta);
+ }
+ return ioMeta;
+ }
+
@Override
public void searchInfoAndTargetTransforms(List<TransformMeta> transforms) {
ITransformIOMeta ioMeta = getTransformIOMeta();
- ioMeta.getInfoStreams().clear();
+ List<IStream> infoStreams = ioMeta.getInfoStreams();
+
+ String[] prev = null;
+ if (parentTransformMeta != null &&
parentTransformMeta.getParentPipelineMeta() != null) {
+ prev =
parentTransformMeta.getParentPipelineMeta().getPrevTransformNames(parentTransformMeta);
+ }
+
+ // Auto-fill when empty and we have connected transforms
+ if ((inputTransforms == null || inputTransforms.isEmpty())
+ && prev != null
+ && prev.length >= 2) {
+ inputTransforms = new ArrayList<>();
+ for (String p : prev) {
+ inputTransforms.add(p);
+ }
+ setChanged();
+ }
+ if (inputTransforms == null) {
+ inputTransforms = new ArrayList<>();
+ }
+
+ // Clear names that no longer exist in prev; keep and update name when
it's a rename (stream's
+ // transform is in prev)
+ if (prev != null) {
+ List<String> newInputTransforms = new ArrayList<>();
+ List<String> newKeyFields = (keyFields != null) ? new ArrayList<>() :
null;
+ for (int i = 0; i < inputTransforms.size(); i++) {
+ String name = inputTransforms.get(i);
+ if (Utils.isEmpty(name) || ArrayUtils.contains(prev, name)) {
+ newInputTransforms.add(name);
+ if (newKeyFields != null && i < keyFields.size()) {
+ newKeyFields.add(keyFields.get(i));
+ }
+ } else if (i < infoStreams.size()) {
+ IStream stream = infoStreams.get(i);
+ if (stream.getTransformMeta() != null
+ && ArrayUtils.contains(prev, stream.getTransformName())) {
+ // Renamed: keep entry with updated name
+ newInputTransforms.add(stream.getTransformName());
+ if (newKeyFields != null && i < keyFields.size()) {
+ newKeyFields.add(keyFields.get(i));
+ }
+ setChanged();
+ }
+ } else {
+ setChanged();
+ }
+ }
+ inputTransforms.clear();
+ inputTransforms.addAll(newInputTransforms);
+ if (keyFields != null) {
+ keyFields.clear();
+ keyFields.addAll(newKeyFields);
+ }
+ }
+
+ // Resolve each slot and build the list of streams to set
(getInfoStreams() returns a copy, so
+ // we must replace via clearStreams + addStream)
+ List<IStream> resolvedStreams = new ArrayList<>();
+ String streamDescription = BaseMessages.getString(PKG,
"MultiMergeJoin.InfoStream.Description");
+
for (int i = 0; i < inputTransforms.size(); i++) {
- String inputTransformName = inputTransforms.get(i);
- ioMeta.addStream(
- new Stream(
- StreamType.INFO,
- TransformMeta.findTransform(transforms, inputTransformName),
- BaseMessages.getString(PKG,
"MultiMergeJoin.InfoStream.Description"),
- StreamIcon.INFO,
- inputTransformName));
+ String name = inputTransforms.get(i);
+ IStream existingStream = (i < infoStreams.size()) ? infoStreams.get(i) :
null;
+
+ boolean nameStale =
+ Utils.isEmpty(name)
+ || (prev != null && !ArrayUtils.contains(prev, name))
+ || TransformMeta.findTransform(transforms, name) == null;
+ boolean preferStream =
+ existingStream != null
+ && existingStream.getTransformMeta() != null
+ && prev != null
+ && ArrayUtils.contains(prev, existingStream.getTransformName())
+ && nameStale;
+
+ TransformMeta tm = null;
+ if (preferStream) {
+ name = existingStream.getTransformName();
+ inputTransforms.set(i, name);
+ tm = existingStream.getTransformMeta();
+ setChanged();
+ }
+ if (tm == null) {
+ tm = TransformMeta.findTransform(transforms, name);
+ if (tm == null && existingStream != null &&
existingStream.getTransformMeta() != null) {
+ name = existingStream.getTransformName();
+ inputTransforms.set(i, name);
+ tm = TransformMeta.findTransform(transforms, name);
+ }
+ }
+ String subject = (tm != null) ? tm.getName() : null;
+ resolvedStreams.add(
+ new Stream(StreamType.INFO, tm, streamDescription, StreamIcon.INFO,
subject));
+ }
+
+ // Sync keyFields size
+ if (keyFields != null) {
+ while (keyFields.size() > inputTransforms.size()) {
+ keyFields.remove(keyFields.size() - 1);
+ }
+ while (keyFields.size() < inputTransforms.size()) {
+ keyFields.add("");
+ }
+ }
+
+ // Replace ioMeta streams so the pipeline sees INFO streams (like Merge
Join / Append)
+ if (ioMeta instanceof TransformIOMeta) {
+ ((TransformIOMeta) ioMeta).clearStreams();
+ for (IStream s : resolvedStreams) {
+ ioMeta.addStream(s);
+ }
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
index 70d83168aa..098715e909 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
@@ -2351,12 +2351,12 @@ public class HopGuiPipelineGraph extends
HopGuiAbstractGraph
PipelineHopMeta fromHop = pipelineMeta.findPipelineHopTo(transformMeta);
PipelineHopMeta toHop = pipelineMeta.findPipelineHopFrom(transformMeta);
+ List<PipelineHopMeta> removedHops = new ArrayList<>();
for (int i = pipelineMeta.nrPipelineHops() - 1; i >= 0; i--) {
PipelineHopMeta hop = pipelineMeta.getPipelineHop(i);
if (transformMeta.equals(hop.getFromTransform())
|| transformMeta.equals(hop.getToTransform())) {
- // Transform is connected with a hop, remove this hop.
- //
+ removedHops.add(hop);
hopGui.undoDelegate.addUndoNew(pipelineMeta, new PipelineHopMeta[]
{hop}, new int[] {i});
pipelineMeta.removePipelineHop(i);
}
@@ -2364,9 +2364,41 @@ public class HopGuiPipelineGraph extends
HopGuiAbstractGraph
// If the transform was part of a chain, re-connect it.
//
+ TransformMeta newUpstream = null;
if (fromHop != null && toHop != null) {
+ newUpstream = fromHop.getFromTransform();
pipelineHopDelegate.newHop(
- pipelineMeta, new PipelineHopMeta(fromHop.getFromTransform(),
toHop.getToTransform()));
+ pipelineMeta, new PipelineHopMeta(newUpstream,
toHop.getToTransform()));
+
+ // Same as split hop (insertTransform) but in reverse: point the
target's info streams that
+ // were reading from the detached transform to the new upstream so
+ // searchInfoAndTargetTransforms
+ // can resolve correctly.
+ TransformMeta targetTransform = toHop.getToTransform();
+ if (targetTransform != null
+ && targetTransform.getTransform() != null
+ && newUpstream != null) {
+ ITransformIOMeta toIo =
targetTransform.getTransform().getTransformIOMeta();
+ if (toIo != null) {
+ for (IStream stream : toIo.getInfoStreams()) {
+ if (stream.getTransformMeta() != null
+ && stream.getTransformMeta().equals(transformMeta)) {
+ stream.setTransformMeta(newUpstream);
+ stream.setSubject(newUpstream.getName());
+ targetTransform.getTransform().handleStreamSelection(stream);
+ }
+ }
+ }
+ }
+ }
+
+ // Same as split hop (insertTransform): after topology change, trigger IO
meta update on
+ // targets of removed hops so linked transform names are updated.
+ for (PipelineHopMeta hop : removedHops) {
+ TransformMeta toTransform = hop.getToTransform();
+ if (toTransform != null && toTransform.getTransform() != null) {
+
toTransform.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+ }
}
updateGui();
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineTransformDelegate.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineTransformDelegate.java
index a5b77c5fae..f962e743b0 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineTransformDelegate.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineTransformDelegate.java
@@ -474,6 +474,8 @@ public class HopGuiPipelineTransformDelegate {
if (stream.getTransformMeta() != null &&
stream.getTransformMeta().equals(toTransform)) {
// This target stream was directed to B, now we need to direct it to C
stream.setTransformMeta(transformMeta);
+ // Update subject so searchInfoAndTargetTransforms resolves to C
+ stream.setSubject(transformMeta.getName());
fromTransform.getTransform().handleStreamSelection(stream);
}
}
@@ -483,8 +485,10 @@ public class HopGuiPipelineTransformDelegate {
ITransformIOMeta toIo = toTransform.getTransform().getTransformIOMeta();
for (IStream stream : toIo.getInfoStreams()) {
if (stream.getTransformMeta() != null &&
stream.getTransformMeta().equals(fromTransform)) {
- // This info stream was reading from B, now we need to direct it to C
+ // This info stream was reading from A, now we need to direct it to C
stream.setTransformMeta(transformMeta);
+ // Update subject so searchInfoAndTargetTransforms (e.g. Stream
Lookup) resolves to C
+ stream.setSubject(transformMeta.getName());
toTransform.getTransform().handleStreamSelection(stream);
}
}
@@ -512,7 +516,6 @@ public class HopGuiPipelineTransformDelegate {
newHop2.setEnabled(hop.isEnabled());
if (pipelineMeta.findPipelineHop(newHop2) == null) {
pipelineMeta.addPipelineHop(newHop2);
-
toTransform.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
hopGui.undoDelegate.addUndoNew(
pipelineMeta,
new PipelineHopMeta[] {newHop2},
@@ -520,6 +523,8 @@ public class HopGuiPipelineTransformDelegate {
true);
}
+ // Remove old hop before searchInfoAndTargetTransforms so "prev" reflects
new topology
+ // (e.g. Merge Join's info stream can detect insert-in-the-middle).
hopGui.undoDelegate.addUndoDelete(
pipelineMeta,
new PipelineHopMeta[] {hop},
@@ -527,6 +532,8 @@ public class HopGuiPipelineTransformDelegate {
true);
pipelineMeta.removePipelineHop(hop);
+
toTransform.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+
return transformMeta;
}