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 8f5ff23a68 detect stram removal and cleanup transform, fixes #6376 
(#6377)
8f5ff23a68 is described below

commit 8f5ff23a6877c51cb36665450ea6cbb239f7e6e8
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Jan 14 16:00:57 2026 +0100

    detect stram removal and cleanup transform, fixes #6376 (#6377)
---
 .../transforms/multimerge/MultiMergeJoin.java      |  44 +++---
 .../multimerge/MultiMergeJoinDialog.java           |  50 ++-----
 .../transforms/multimerge/MultiMergeJoinMeta.java  |  53 ++++++--
 .../multimerge/MultiMergeJoinMetaTest.java         | 148 +++++++++++++++++++++
 .../MultiMergeJoinMetaInjectionTest.java           |  40 ------
 .../multimerge/MultiMergeJoinMetaTest.java         | 127 ------------------
 .../delegates/HopGuiPipelineHopDelegate.java       |   7 +-
 .../delegates/HopGuiPipelineTransformDelegate.java |  12 ++
 8 files changed, 240 insertions(+), 241 deletions(-)

diff --git 
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoin.java
 
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoin.java
index 0f39e5a62f..11753cb4da 100644
--- 
a/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoin.java
+++ 
b/plugins/transforms/multimerge/src/main/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoin.java
@@ -32,9 +32,7 @@ import org.apache.hop.pipeline.Pipeline;
 import org.apache.hop.pipeline.PipelineHopMeta;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransform;
-import org.apache.hop.pipeline.transform.ITransformIOMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.apache.hop.pipeline.transform.stream.IStream;
 
 /**
  * Merge rows from 2 sorted streams and output joined rows with matched key 
fields. Use this instead
@@ -68,10 +66,6 @@ public class MultiMergeJoin extends 
BaseTransform<MultiMergeJoinMeta, MultiMerge
       throws HopException {
 
     PipelineHopMeta pipelineHopMeta;
-
-    ITransformIOMeta transformIOMeta = meta.getTransformIOMeta();
-    List<IStream> infoStreams = transformIOMeta.getInfoStreams();
-    IStream stream;
     TransformMeta toTransformMeta = meta.getParentTransformMeta();
     TransformMeta fromTransformMeta;
 
@@ -79,23 +73,20 @@ public class MultiMergeJoin extends 
BaseTransform<MultiMergeJoinMeta, MultiMerge
     List<String> inputTransformNames = meta.getInputTransforms();
     String inputTransformName;
 
-    for (int i = 0; i < infoStreams.size(); i++) {
+    for (int i = 0; i < inputTransformNames.size(); i++) {
       inputTransformName = inputTransformNames.get(i);
-      stream = infoStreams.get(i);
-      fromTransformMeta = stream.getTransformMeta();
+
+      fromTransformMeta = getPipelineMeta().findTransform(inputTransformName);
       if (fromTransformMeta == null) {
-        // should not arrive here, shoud typically have been caught by init.
         throw new HopException(
             BaseMessages.getString(
                 PKG,
                 CONST_MULTI_MERGE_JOIN_LOG_UNABLE_TO_FIND_REFERENCE_STREAM,
                 inputTransformName));
       }
-      // check the hop
+
       pipelineHopMeta = getPipelineMeta().findPipelineHop(fromTransformMeta, 
toTransformMeta, true);
-      // there is no hop: this is unexpected.
       if (pipelineHopMeta == null) {
-        // should not arrive here, shoud typically have been caught by init.
         throw new HopException(
             BaseMessages.getString(
                 PKG,
@@ -168,7 +159,14 @@ public class MultiMergeJoin extends 
BaseTransform<MultiMergeJoinMeta, MultiMerge
         queueEntry.row = row;
         rowMeta = rowSet.getRowMeta();
 
-        keyField = meta.getKeyFields().get(i);
+        List<String> keyFields = meta.getKeyFields();
+        if (keyFields == null || i >= keyFields.size()) {
+          throw new HopException(
+              String.format(
+                  "Key fields configuration missing for input transform '%s' 
at index %d",
+                  inputTransformName, i));
+        }
+        keyField = keyFields.get(i);
         String[] keyFieldParts = keyField.split(",");
         String keyFieldPart;
         data.keyNrs[j] = new int[keyFieldParts.length];
@@ -396,15 +394,16 @@ public class MultiMergeJoin extends 
BaseTransform<MultiMergeJoinMeta, MultiMerge
   public boolean init() {
 
     if (super.init()) {
-      ITransformIOMeta transformIOMeta = meta.getTransformIOMeta();
       List<String> inputTransformNames = meta.getInputTransforms();
-      String inputTransformName;
-      List<IStream> infoStreams = transformIOMeta.getInfoStreams();
-      IStream stream;
-      for (int i = 0; i < infoStreams.size(); i++) {
-        inputTransformName = inputTransformNames.get(i);
-        stream = infoStreams.get(i);
-        if (stream.getTransformMeta() == null) {
+
+      if (inputTransformNames == null || inputTransformNames.isEmpty()) {
+        logError("No input transforms configured for multiway merge join");
+        return false;
+      }
+
+      for (String inputTransformName : inputTransformNames) {
+        TransformMeta transformMeta = 
getPipelineMeta().findTransform(inputTransformName);
+        if (transformMeta == null) {
           logError(
               BaseMessages.getString(
                   PKG,
@@ -413,6 +412,7 @@ public class MultiMergeJoin extends 
BaseTransform<MultiMergeJoinMeta, MultiMerge
           return false;
         }
       }
+
       String joinType = meta.getJoinType();
       for (int i = 0; i < MultiMergeJoinMeta.joinTypes.length; ++i) {
         if (joinType.equalsIgnoreCase(MultiMergeJoinMeta.joinTypes[i])) {
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 eccf4f5335..45583b643e 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
@@ -26,12 +26,7 @@ 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.ITransformIOMeta;
 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;
 import org.apache.hop.ui.core.ConstUi;
 import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
@@ -83,12 +78,11 @@ public class MultiMergeJoinDialog extends 
BaseTransformDialog {
     joinMeta = transformMeta;
 
     allInputTransforms = getInputTransformNames();
-    int numInputs =
-        Math.max(
-            2,
-            joinMeta.getInputTransforms() != null
-                ? joinMeta.getInputTransforms().size()
-                : pipelineMeta.getPrevTransformNames(transformName).length);
+
+    int availableTransforms = allInputTransforms.length;
+    int configuredTransforms =
+        (joinMeta.getInputTransforms() != null) ? 
joinMeta.getInputTransforms().size() : 0;
+    int numInputs = Math.max(2, Math.max(availableTransforms, 
configuredTransforms));
 
     wInputTransformArray = new CCombo[numInputs];
     keyValTextBox = new Text[numInputs];
@@ -448,6 +442,12 @@ public class MultiMergeJoinDialog extends 
BaseTransformDialog {
         widgetIndex++;
       }
 
+      while (widgetIndex < wInputTransformArray.length) {
+        wInputTransformArray[widgetIndex].setText("");
+        keyValTextBox[widgetIndex].setText("");
+        widgetIndex++;
+      }
+
       String joinType = joinMeta.getJoinType();
       if (!Utils.isEmpty(joinType)) {
         joinTypeCombo.setText(joinType);
@@ -466,20 +466,12 @@ public class MultiMergeJoinDialog extends 
BaseTransformDialog {
     dispose();
   }
 
-  /**
-   * Get the meta data
-   *
-   * @param meta metadata to fetch information from
-   */
   private void getMeta(MultiMergeJoinMeta meta) {
-    ITransformIOMeta transformIOMeta = meta.getTransformIOMeta();
-    List<IStream> infoStreams = transformIOMeta.getInfoStreams();
-    IStream stream;
-    String streamDescription;
     ArrayList<String> inputTransformNameList = new ArrayList<>();
     ArrayList<String> keyList = new ArrayList<>();
     CCombo wInputTransform;
     String inputTransformName;
+
     for (int i = 0; i < wInputTransformArray.length; i++) {
       wInputTransform = wInputTransformArray[i];
       inputTransformName = wInputTransform.getText();
@@ -490,28 +482,12 @@ public class MultiMergeJoinDialog extends 
BaseTransformDialog {
 
       inputTransformNameList.add(inputTransformName);
       keyList.add(keyValTextBox[i].getText());
-
-      if (infoStreams.size() < inputTransformNameList.size()) {
-        streamDescription = BaseMessages.getString(PKG, 
"MultiMergeJoin.InfoStream.Description");
-        stream = new Stream(StreamType.INFO, null, streamDescription, 
StreamIcon.INFO, null);
-        transformIOMeta.addStream(stream);
-      }
     }
 
-    // Save the input transforms and key fields in the order they appear in 
the UI
     meta.setInputTransforms(inputTransformNameList);
     meta.setKeyFields(keyList);
-
-    int inputTransformCount = inputTransformNameList.size();
-
-    infoStreams = transformIOMeta.getInfoStreams();
-    for (int i = 0; i < inputTransformCount; i++) {
-      inputTransformName = inputTransformNameList.get(i);
-      stream = infoStreams.get(i);
-      stream.setTransformMeta(pipelineMeta.findTransform(inputTransformName));
-    }
-
     meta.setJoinType(joinTypeCombo.getText());
+    meta.searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
   }
 
   private void ok() {
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 79b3e3c6ec..5da674d066 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
@@ -17,6 +17,7 @@
 
 package org.apache.hop.pipeline.transforms.multimerge;
 
+import java.util.ArrayList;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
@@ -56,11 +57,14 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
   public static final String[] joinTypes = {"INNER", "FULL OUTER"};
   public static final boolean[] optionals = {false, true};
 
-  @HopMetadataProperty(key = "join_type", injectionKey = "JOIN_TYPE")
+  @HopMetadataProperty(
+      key = "join_type",
+      injectionKey = "JOIN_TYPE",
+      injectionKeyDescription = "MultiMergeJoinMeta.Injection.JoinType")
   private String joinType;
 
   /** comma separated key values for each stream */
-  @HopMetadataProperty(key = "key", groupKey = "keys", injectionKey = 
"JOIN_TYPE")
+  @HopMetadataProperty(key = "key", groupKey = "keys", injectionKey = 
"KEY_FIELDS")
   private List<String> keyFields;
 
   /** input stream names */
@@ -76,7 +80,9 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
   }
 
   public MultiMergeJoinMeta() {
-    super(); // allocate BaseTransformMeta
+    super();
+    this.keyFields = new ArrayList<>();
+    this.inputTransforms = new ArrayList<>();
   }
 
   /**
@@ -126,15 +132,13 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
     ioMeta.getInfoStreams().clear();
     for (int i = 0; i < inputTransforms.size(); i++) {
       String inputTransformName = inputTransforms.get(i);
-      if (i >= ioMeta.getInfoStreams().size()) {
-        ioMeta.addStream(
-            new Stream(
-                StreamType.INFO,
-                TransformMeta.findTransform(transforms, inputTransformName),
-                BaseMessages.getString(PKG, 
"MultiMergeJoin.InfoStream.Description"),
-                StreamIcon.INFO,
-                inputTransformName));
-      }
+      ioMeta.addStream(
+          new Stream(
+              StreamType.INFO,
+              TransformMeta.findTransform(transforms, inputTransformName),
+              BaseMessages.getString(PKG, 
"MultiMergeJoin.InfoStream.Description"),
+              StreamIcon.INFO,
+              inputTransformName));
     }
   }
 
@@ -190,4 +194,29 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
   public void resetTransformIoMeta() {
     // Don't reset!
   }
+
+  @Override
+  public boolean cleanAfterHopToRemove(TransformMeta fromTransform) {
+    if (fromTransform == null || fromTransform.getName() == null) {
+      return false;
+    }
+
+    if (inputTransforms == null || inputTransforms.isEmpty()) {
+      return false;
+    }
+
+    String fromTransformName = fromTransform.getName();
+
+    for (int i = 0; i < inputTransforms.size(); i++) {
+      if (fromTransformName.equals(inputTransforms.get(i))) {
+        inputTransforms.remove(i);
+        if (keyFields != null && i < keyFields.size()) {
+          keyFields.remove(i);
+        }
+        return true;
+      }
+    }
+
+    return false;
+  }
 }
diff --git 
a/plugins/transforms/multimerge/src/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
 
b/plugins/transforms/multimerge/src/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
new file mode 100644
index 0000000000..5a5157d2d7
--- /dev/null
+++ 
b/plugins/transforms/multimerge/src/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hop.pipeline.transforms.multimerge;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class MultiMergeJoinMetaTest {
+  private MultiMergeJoinMeta multiMergeMeta;
+
+  @RegisterExtension
+  static RestoreHopEngineEnvironmentExtension env = new 
RestoreHopEngineEnvironmentExtension();
+
+  @BeforeEach
+  void setUp() {
+    multiMergeMeta = new MultiMergeJoinMeta();
+  }
+
+  @Test
+  void testSetGetInputTransforms() {
+    // Constructor now initializes to empty list, not null
+    assertTrue(multiMergeMeta.getInputTransforms().isEmpty());
+    List<String> inputTransforms = Arrays.asList("Transform1", "Transform2");
+    multiMergeMeta.setInputTransforms(inputTransforms);
+    assertEquals(inputTransforms, multiMergeMeta.getInputTransforms());
+  }
+
+  @Test
+  void testClone() throws Exception {
+    MultiMergeJoinMeta meta = new MultiMergeJoinMeta();
+    meta.setKeyFields(Arrays.asList("key1", "key2"));
+    meta.setInputTransforms(Arrays.asList("transform1", "transform2", 
"transform3"));
+    // scalars should be cloned using super.clone() - makes sure they're 
calling super.clone()
+    meta.setJoinType("INNER");
+    MultiMergeJoinMeta aClone = (MultiMergeJoinMeta) meta.clone();
+    assertFalse(aClone == meta);
+    assertEquals(meta.getKeyFields(), aClone.getKeyFields());
+    assertEquals(meta.getInputTransforms(), aClone.getInputTransforms());
+    assertEquals(meta.getJoinType(), aClone.getJoinType());
+  }
+
+  @Test
+  void testCleanAfterHopToRemove() {
+    MultiMergeJoinMeta meta = new MultiMergeJoinMeta();
+
+    // Setup: Create a meta with 3 input transforms and their corresponding 
key fields
+    List<String> inputTransforms = new ArrayList<>();
+    inputTransforms.add("Transform1");
+    inputTransforms.add("Transform2");
+    inputTransforms.add("Transform3");
+    meta.setInputTransforms(inputTransforms);
+
+    List<String> keyFields = new ArrayList<>();
+    keyFields.add("key1");
+    keyFields.add("key2");
+    keyFields.add("key3");
+    meta.setKeyFields(keyFields);
+
+    // Create a TransformMeta to represent the transform being removed
+    TransformMeta removedTransform = new TransformMeta();
+    removedTransform.setName("Transform2");
+
+    // Act: Remove the transform
+    boolean changed = meta.cleanAfterHopToRemove(removedTransform);
+
+    // Assert: Verify the transform was removed
+    assertTrue(changed, "cleanAfterHopToRemove should return true when a 
transform is removed");
+    assertEquals(2, meta.getInputTransforms().size(), "Should have 2 input 
transforms remaining");
+    assertEquals(2, meta.getKeyFields().size(), "Should have 2 key fields 
remaining");
+
+    // Verify the correct transform was removed
+    assertEquals("Transform1", meta.getInputTransforms().get(0));
+    assertEquals("Transform3", meta.getInputTransforms().get(1));
+
+    // Verify the corresponding key field was removed
+    assertEquals("key1", meta.getKeyFields().get(0));
+    assertEquals("key3", meta.getKeyFields().get(1));
+  }
+
+  @Test
+  void testCleanAfterHopToRemove_NonExistentTransform() {
+    MultiMergeJoinMeta meta = new MultiMergeJoinMeta();
+
+    // Setup: Create a meta with input transforms
+    List<String> inputTransforms = new ArrayList<>();
+    inputTransforms.add("Transform1");
+    inputTransforms.add("Transform2");
+    meta.setInputTransforms(inputTransforms);
+
+    List<String> keyFields = new ArrayList<>();
+    keyFields.add("key1");
+    keyFields.add("key2");
+    meta.setKeyFields(keyFields);
+
+    // Create a TransformMeta that doesn't exist in the list
+    TransformMeta nonExistentTransform = new TransformMeta();
+    nonExistentTransform.setName("NonExistent");
+
+    // Act: Try to remove a non-existent transform
+    boolean changed = meta.cleanAfterHopToRemove(nonExistentTransform);
+
+    // Assert: Verify nothing was changed
+    assertFalse(changed, "cleanAfterHopToRemove should return false when 
transform not found");
+    assertEquals(2, meta.getInputTransforms().size(), "Should still have 2 
input transforms");
+    assertEquals(2, meta.getKeyFields().size(), "Should still have 2 key 
fields");
+  }
+
+  @Test
+  void testCleanAfterHopToRemove_NullTransform() {
+    MultiMergeJoinMeta meta = new MultiMergeJoinMeta();
+
+    // Setup: Create a meta with input transforms
+    List<String> inputTransforms = new ArrayList<>();
+    inputTransforms.add("Transform1");
+    meta.setInputTransforms(inputTransforms);
+
+    // Act: Try to remove a null transform
+    boolean changed = meta.cleanAfterHopToRemove(null);
+
+    // Assert: Verify nothing was changed
+    assertFalse(changed, "cleanAfterHopToRemove should return false when 
transform is null");
+    assertEquals(1, meta.getInputTransforms().size(), "Should still have 1 
input transform");
+  }
+}
diff --git 
a/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaInjectionTest.java
 
b/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaInjectionTest.java
deleted file mode 100644
index ac8790bd43..0000000000
--- 
a/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaInjectionTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hop.pipeline.transforms.multimerge;
-
-import org.apache.hop.core.injection.BaseMetadataInjectionTest;
-import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
- class MultiMergeJoinMetaInjectionTest extends 
BaseMetadataInjectionTestJunit5<MultiMergeJoinMeta> {
-  @RegisterExtension static RestoreHopEngineEnvironmentExtension env = new 
RestoreHopEngineEnvironmentExtension();
-
-  @BeforeEach
- void setup() throws Exception {
-    setup(new MultiMergeJoinMeta());
-  }
-
-  @Test
- void test() throws Exception {
-    check("JOIN_TYPE", () -> meta.getJoinType());
-    check("KEY_FIELDS", () -> meta.getKeyFields()[0]);
-    check("INPUT_TRANSFORMS", () -> meta.getInputTransforms()[0]);
-  }
-}
diff --git 
a/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
 
b/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
deleted file mode 100644
index a42deccc36..0000000000
--- 
a/plugins/transforms/multimerge/test/java/org/apache/hop/pipeline/transforms/multimerge/MultiMergeJoinMetaTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hop.pipeline.transforms.multimerge;
-
-import org.apache.hop.core.HopEnvironment;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.plugins.PluginRegistry;
-import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
-import org.apache.hop.pipeline.transform.ITransform;
-import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
-import org.apache.hop.pipeline.transforms.loadsave.initializer.IInitializer;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.ArrayLoadSaveValidator;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
-import 
org.apache.hop.pipeline.transforms.loadsave.validator.StringLoadSaveValidator;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
- class MultiMergeJoinMetaTest implements IInitializer<ITransform> {
-  LoadSaveTester loadSaveTester;
-  Class<MultiMergeJoinMeta> testMetaClass = MultiMergeJoinMeta.class;
-  private MultiMergeJoinMeta multiMergeMeta;
-  @RegisterExtension static RestoreHopEngineEnvironmentExtension env = new 
RestoreHopEngineEnvironmentExtension();
-
-  @BeforeEach
- void setUpLoadSave() throws Exception {
-    HopEnvironment.init();
-    PluginRegistry.init();
-    multiMergeMeta = new MultiMergeJoinMeta();
-    List<String> attributes = Arrays.asList("joinType", "keyFields", 
"inputTransforms");
-
-    Map<String, String> getterMap = new HashMap<>();
-    Map<String, String> setterMap = new HashMap<>();
-    IFieldLoadSaveValidator<String[]> stringArrayLoadSaveValidator =
-        new ArrayLoadSaveValidator<String>(new StringLoadSaveValidator(), 5);
-
-    Map<String, IFieldLoadSaveValidator<?>> attrValidatorMap =
-        new HashMap<String, IFieldLoadSaveValidator<?>>();
-    attrValidatorMap.put("keyFields", stringArrayLoadSaveValidator);
-    attrValidatorMap.put("inputTransforms", stringArrayLoadSaveValidator);
-
-    Map<String, IFieldLoadSaveValidator<?>> typeValidatorMap =
-        new HashMap<String, IFieldLoadSaveValidator<?>>();
-
-    loadSaveTester =
-        new LoadSaveTester(
-            testMetaClass,
-            attributes,
-            new ArrayList<>(),
-            getterMap,
-            setterMap,
-            attrValidatorMap,
-            typeValidatorMap,
-            this);
-  }
-
-  // Call the allocate method on the LoadSaveTester meta class
-  @Override
-  public void modify(ITransform someMeta) {
-    if (someMeta instanceof MultiMergeJoinMeta) {
-      ((MultiMergeJoinMeta) someMeta).allocateKeys(5);
-      ((MultiMergeJoinMeta) someMeta).allocateInputTransforms(5);
-    }
-  }
-
-  @Test
- void testSerialization() throws HopException {
-    loadSaveTester.testSerialization();
-  }
-
-  @Test
- void testSetGetInputTransforms() {
-    assertNull(multiMergeMeta.getInputTransforms());
-    String[] inputTransforms = new String[] {"Transform1", "Transform2"};
-    multiMergeMeta.setInputTransforms(inputTransforms);
-    assertArrayEquals(inputTransforms, multiMergeMeta.getInputTransforms());
-  }
-
-  @Test
- void testGetXml() {
-    String[] inputTransforms = new String[] {"Transform1", "Transform2"};
-    multiMergeMeta.setInputTransforms(inputTransforms);
-    multiMergeMeta.setKeyFields(new String[] {"Key1", "Key2"});
-    String xml = multiMergeMeta.getXml();
-    assertTrue(xml.contains("transform0"));
-    assertTrue(xml.contains("transform1"));
-  }
-
-  @Test
- void cloneTest() throws Exception {
-    MultiMergeJoinMeta meta = new MultiMergeJoinMeta();
-    meta.allocateKeys(2);
-    meta.allocateInputTransforms(3);
-    meta.setKeyFields(new String[] {"key1", "key2"});
-    meta.setInputTransforms(new String[] {"transform1", "transform2", 
"transform3"});
-    // scalars should be cloned using super.clone() - makes sure they're 
calling super.clone()
-    meta.setJoinType("INNER");
-    MultiMergeJoinMeta aClone = (MultiMergeJoinMeta) meta.clone();
-    assertFalse(aClone == meta);
-    assertTrue(Arrays.equals(meta.getKeyFields(), aClone.getKeyFields()));
-    assertTrue(Arrays.equals(meta.getInputTransforms(), 
aClone.getInputTransforms()));
-    assertEquals(meta.getJoinType(), aClone.getJoinType());
-  }
-}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineHopDelegate.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineHopDelegate.java
index 2dd0e7398c..591d25e382 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineHopDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineHopDelegate.java
@@ -227,15 +227,16 @@ public class HopGuiPipelineHopDelegate {
     TransformMeta toTransformMeta = pipelineHopMeta.getToTransform();
     TransformMeta beforeTo = (TransformMeta) toTransformMeta.clone();
     int indexTo = pipelineMeta.indexOfTransform(toTransformMeta);
-    if (toTransformMeta.getTransform() != null) {
-      
toTransformMeta.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
-    }
 
     boolean transformFromNeedAddUndoChange =
         
fromTransformMeta.getTransform().cleanAfterHopFromRemove(pipelineHopMeta.getToTransform());
     boolean transformToNeedAddUndoChange =
         
toTransformMeta.getTransform().cleanAfterHopToRemove(fromTransformMeta);
 
+    if (toTransformMeta.getTransform() != null) {
+      
toTransformMeta.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+    }
+
     // If this is an error handling hop, disable it
     //
     if (pipelineHopMeta.getFromTransform().isDoingErrorHandling()) {
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 633e07a742..a5b77c5fae 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
@@ -700,6 +700,18 @@ public class HopGuiPipelineTransformDelegate {
           int idx = pipelineMeta.indexOfPipelineHop(hi);
           pipelineHops.add((PipelineHopMeta) hi.clone());
           hopIndexes[hopIndex] = idx;
+
+          TransformMeta fromTransform = hi.getFromTransform();
+          TransformMeta toTransform = hi.getToTransform();
+
+          if (!transforms.contains(toTransform) && toTransform.getTransform() 
!= null) {
+            toTransform.getTransform().cleanAfterHopToRemove(fromTransform);
+            
toTransform.getTransform().searchInfoAndTargetTransforms(pipelineMeta.getTransforms());
+          }
+          if (!transforms.contains(fromTransform) && 
fromTransform.getTransform() != null) {
+            fromTransform.getTransform().cleanAfterHopFromRemove(toTransform);
+          }
+
           pipelineMeta.removePipelineHop(idx);
           hopIndex++;
           break;

Reply via email to