This is an automated email from the ASF dual-hosted git repository.

mcasters 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 9b6313f390 IT and 2.17 compatibility fixes (#6909)
9b6313f390 is described below

commit 9b6313f390142245ffc9f538f328090cbe43943e
Author: Matt Casters <[email protected]>
AuthorDate: Tue Mar 31 15:17:29 2026 +0200

    IT and 2.17 compatibility fixes (#6909)
    
    * Issue #6884 (2.17 compatibility fixes)
    
    * Issue #6884 (2.17 mongo collection tag fix)
    
    * Issue #6884 (script fix)
    
    * IT fixes
    
    extra null checks
    
    * Issue #6884 (spotless)
    
    ---------
    
    Co-authored-by: Hans Van Akelyen <[email protected]>
---
 .../hop/metadata/inject/HopMetadataInjector.java   |   6 +
 .../metadata/serializer/xml/XmlMetadataUtil.java   |   3 +
 .../metadata/inject/HopMetadataInjectorTest.java   |  48 +++++
 .../serializer/xml/XmlMetadataUtilTest.java        |  22 +++
 .../java/org/apache/hop/pipeline/PipelineMeta.java |   4 +-
 .../hop_server/0005-verify-executions-logged.hpl   | 208 ++++++++++-----------
 .../0008-email-messages-connection-golden.csv      |   2 +-
 .../0009-getConnectionValues.hpl                   |   2 +-
 .../parameters_and_variables/run-all.hwf           | 158 ++++++----------
 .../transforms/memgroupby/MemoryGroupByMeta.java   |   4 +-
 .../apache/hop/mongo/wrapper/field/MongoField.java |   2 +-
 .../mongodboutput/MongoDbOutputMeta.java           |  24 +--
 .../apache/hop/mongo/wrapper/MongoFieldTest.java   |   8 +
 .../MongoDbDeleteMetaInjectionTest.java            |  57 ++++++
 .../mongodbdelete/MongoDbDeleteMetaTest.java       |  85 +++++++++
 .../transforms/multimerge/MultiMergeJoinMeta.java  |  44 +----
 .../mysqlbulkloader/MySqlBulkLoaderMeta.java       |  45 ++---
 .../pipelineexecutor/PipelineExecutorMeta.java     |  39 ++--
 .../hop/pipeline/transforms/script/Script.java     |   9 +-
 .../hop/pipeline/transforms/script/ScriptData.java |   1 +
 .../sqlfileoutput/SQLFileOutputMeta.java           |  25 +--
 .../workflowexecutor/WorkflowExecutorMeta.java     |  39 ++--
 .../transforms/writetolog/WriteToLogMeta.java      |  22 +--
 .../perspective/explorer/ExplorerPerspective.java  |  21 ++-
 24 files changed, 497 insertions(+), 381 deletions(-)

diff --git 
a/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java 
b/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
index b29c0134ff..c20ca7952a 100644
--- a/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
+++ b/core/src/main/java/org/apache/hop/metadata/inject/HopMetadataInjector.java
@@ -37,6 +37,7 @@ import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.RowBuffer;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IEnumHasCode;
+import org.apache.hop.metadata.api.IEnumHasCodeAndDescription;
 import org.apache.hop.metadata.api.IHopMetadata;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.metadata.api.IHopMetadataSerializer;
@@ -474,6 +475,11 @@ public class HopMetadataInjector {
     final Class<? extends IEnumHasCode> enumerationClass =
         (Class<? extends IEnumHasCode>) field.getType();
     fieldValue = IEnumHasCode.lookupCode(enumerationClass, code, null);
+    if (fieldValue == null && 
IEnumHasCodeAndDescription.class.isAssignableFrom(enumerationClass)) {
+      fieldValue =
+          IEnumHasCodeAndDescription.lookupDescription(
+              (Class<? extends IEnumHasCodeAndDescription>) enumerationClass, 
code, null);
+    }
     if (fieldValue == null) {
       // For backward compatibility, retry with the name as well
       //
diff --git 
a/core/src/main/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtil.java
 
b/core/src/main/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtil.java
index 9c76c2039a..e857234f0f 100644
--- 
a/core/src/main/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtil.java
+++ 
b/core/src/main/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtil.java
@@ -723,6 +723,9 @@ public class XmlMetadataUtil {
     // What is the name of the object to reference?
     //
     String name = XmlHandler.getTagValue(node, tag);
+    if (StringUtils.isEmpty(name)) {
+      return null;
+    }
 
     // We need the list to look up the name with.
     //
diff --git 
a/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
 
b/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
index 8d95945e55..704f3d2820 100644
--- 
a/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
+++ 
b/core/src/test/java/org/apache/hop/metadata/inject/HopMetadataInjectorTest.java
@@ -42,6 +42,8 @@ import org.apache.hop.core.row.value.ValueMetaNumber;
 import org.apache.hop.core.row.value.ValueMetaPlugin;
 import org.apache.hop.core.row.value.ValueMetaPluginType;
 import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.IEnumHasCodeAndDescription;
 import org.apache.hop.metadata.inject.company.Company;
 import org.apache.hop.metadata.inject.company.Employee;
 import org.apache.hop.metadata.inject.sample.Field;
@@ -222,4 +224,50 @@ class HopMetadataInjectorTest {
     assertTrue(moreFields.contains("FIELD_PRECISION"));
     assertTrue(moreFields.contains("FIELD_TRIM_TYPE"));
   }
+
+  @Test
+  void testStoreWithCodeEnumInjectionWithDescription() throws Exception {
+    Map<String, Object> injectionKeyMap = new HashMap<>();
+    injectionKeyMap.put("TYPE", "Number of Distinct Values (N)");
+
+    DescriptionEnumMeta meta = new DescriptionEnumMeta();
+    HopMetadataInjector.inject(new MemoryMetadataProvider(), meta, 
injectionKeyMap, Map.of());
+
+    assertEquals(DescriptionEnum.COUNT_DISTINCT, meta.getType());
+  }
+
+  private static class DescriptionEnumMeta {
+    @HopMetadataProperty(injectionKey = "TYPE", storeWithCode = true)
+    private DescriptionEnum type;
+
+    public DescriptionEnum getType() {
+      return type;
+    }
+
+    public void setType(DescriptionEnum type) {
+      this.type = type;
+    }
+  }
+
+  private enum DescriptionEnum implements IEnumHasCodeAndDescription {
+    COUNT_DISTINCT("COUNT_DISTINCT", "Number of Distinct Values (N)");
+
+    private final String code;
+    private final String description;
+
+    DescriptionEnum(String code, String description) {
+      this.code = code;
+      this.description = description;
+    }
+
+    @Override
+    public String getCode() {
+      return code;
+    }
+
+    @Override
+    public String getDescription() {
+      return description;
+    }
+  }
 }
diff --git 
a/core/src/test/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtilTest.java
 
b/core/src/test/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtilTest.java
index 2d5870d0b7..796c681f21 100644
--- 
a/core/src/test/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtilTest.java
+++ 
b/core/src/test/java/org/apache/hop/metadata/serializer/xml/XmlMetadataUtilTest.java
@@ -213,4 +213,26 @@ class XmlMetadataUtilTest {
     assertEquals(withCopy.getSteps().size(), listRef.getSteps().size());
     assertEquals(withCopy.getHops().size(), listRef.getHops().size());
   }
+
+  @Test
+  void testListReferenceSerializationWithEmptyReference() throws Exception {
+    String xml =
+        """
+        <steps>
+          
<step><name>S1</name><description>Description1</description><enabled>Y</enabled></step>
+        </steps>
+        <order>
+          <hop><from>S1</from><to/></hop>
+        </order>
+        """;
+    Node node = XmlHandler.loadXmlString("<hop>" + xml + "</hop>", "hop");
+    WithListReference withCopy =
+        XmlMetadataUtil.deSerializeFromXml(
+            node, WithListReference.class, new MemoryMetadataProvider());
+
+    assertEquals(1, withCopy.getSteps().size());
+    assertEquals(1, withCopy.getHops().size());
+    assertEquals("S1", withCopy.getHops().get(0).getFrom().getName());
+    assertNull(withCopy.getHops().get(0).getTo());
+  }
 }
diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java 
b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
index 6f0248edbc..3fb03561a7 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
@@ -70,6 +70,7 @@ import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.core.xml.IXml;
+import org.apache.hop.core.xml.XmlFormatter;
 import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.HopMetadataProperty;
@@ -1442,7 +1443,8 @@ public class PipelineMeta extends AbstractMeta
   @Override
   public String getXml(IVariables variables) throws HopException {
     return XmlHandler.getLicenseHeader(variables)
-        + XmlHandler.aroundTag(XML_TAG, 
XmlMetadataUtil.serializeObjectToXml(this));
+        + XmlFormatter.format(
+            XmlHandler.aroundTag(XML_TAG, 
XmlMetadataUtil.serializeObjectToXml(this)));
   }
 
   /**
diff --git a/integration-tests/hop_server/0005-verify-executions-logged.hpl 
b/integration-tests/hop_server/0005-verify-executions-logged.hpl
index 01a62f871a..709d106bb0 100644
--- a/integration-tests/hop_server/0005-verify-executions-logged.hpl
+++ b/integration-tests/hop_server/0005-verify-executions-logged.hpl
@@ -20,171 +20,159 @@ limitations under the License.
 <pipeline>
   <info>
     <name>0005-verify-executions-logged</name>
-    <name_sync_with_filename>Y</name_sync_with_filename>
-    <description/>
-    <extended_description/>
-    <pipeline_version/>
-    <pipeline_type>Normal</pipeline_type>
-    <parameters>
-    </parameters>
-    <capture_transform_performance>N</capture_transform_performance>
-    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
-    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <modified_date>2023/02/17 13:40:27.812</modified_date>
     <created_user>-</created_user>
     <created_date>2023/02/17 13:40:27.812</created_date>
+    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <parameters>
+</parameters>
+    <pipeline_status>0</pipeline_status>
+    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    <name_sync_with_filename>Y</name_sync_with_filename>
     <modified_user>-</modified_user>
-    <modified_date>2023/02/17 13:40:27.812</modified_date>
+    <capture_transform_performance>N</capture_transform_performance>
+    <pipeline_type>Normal</pipeline_type>
   </info>
-  <notepads>
-  </notepads>
-  <order>
-    <hop>
-      <from>list /tmp/executions</from>
-      <to>count</to>
-      <enabled>Y</enabled>
-    </hop>
-    <hop>
-      <from>count</from>
-      <to>count==0?</to>
-      <enabled>Y</enabled>
-    </hop>
-    <hop>
-      <from>count==0?</from>
-      <to>Nothing logged: abort</to>
-      <enabled>Y</enabled>
-    </hop>
-  </order>
   <transform>
-    <name>list /tmp/executions</name>
     <type>GetFileNames</type>
-    <description/>
-    <distribute>Y</distribute>
-    <custom_distribution/>
+    <attributes>
+</attributes>
+    <GUI>
+      <yloc>96</yloc>
+      <xloc>112</xloc>
+    </GUI>
     <copies>1</copies>
-    <partitioning>
-      <method>none</method>
-      <schema_name/>
-    </partitioning>
+    <name>list /tmp/executions</name>
+    <distribute>Y</distribute>
     <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <filefield>N</filefield>
     <dynamic_include_subfolders>N</dynamic_include_subfolders>
-    <exclude_wildcard_Field/>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <isaddresult>Y</isaddresult>
+    <limit>0</limit>
     <file>
-      <exclude_filemask/>
       <file_required>N</file_required>
-      <filemask>.*</filemask>
-      <include_subfolders>N</include_subfolders>
       <name>${java.io.tmpdir}/executions/</name>
+      <include_subfolders>Y</include_subfolders>
+      <filemask>.*</filemask>
     </file>
-    <filefield>N</filefield>
-    <filename_Field/>
     <filter>
       <filterfiletype>all_files</filterfiletype>
     </filter>
-    <isaddresult>Y</isaddresult>
-    <limit>0</limit>
-    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
     <rownum>N</rownum>
-    <rownum_field/>
-    <wildcard_Field/>
-    <attributes/>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+  </transform>
+  <transform>
+    <type>GroupBy</type>
+    <attributes>
+</attributes>
     <GUI>
-      <xloc>112</xloc>
       <yloc>96</yloc>
+      <xloc>288</xloc>
     </GUI>
-  </transform>
-  <transform>
+    <copies>1</copies>
     <name>count</name>
-    <type>GroupBy</type>
-    <description/>
     <distribute>Y</distribute>
-    <custom_distribution/>
-    <copies>1</copies>
-    <partitioning>
-      <method>none</method>
-      <schema_name/>
-    </partitioning>
-    <add_linenr>N</add_linenr>
-    <all_rows>N</all_rows>
+    <give_back_row>Y</give_back_row>
+    <prefix>grp</prefix>
+    <ignore_aggregate>N</ignore_aggregate>
     <directory>${java.io.tmpdir}</directory>
+    <group>
+</group>
     <fields>
       <field>
+        <type>COUNT_ANY</type>
         <aggregate>count</aggregate>
         <subject>filename</subject>
-        <type>COUNT_ANY</type>
-        <valuefield/>
       </field>
     </fields>
-    <give_back_row>Y</give_back_row>
-    <group>
-</group>
-    <ignore_aggregate>N</ignore_aggregate>
-    <linenr_fieldname/>
-    <prefix>grp</prefix>
-    <attributes/>
+    <all_rows>N</all_rows>
+    <add_linenr>N</add_linenr>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+  </transform>
+  <transform>
+    <type>FilterRows</type>
+    <attributes>
+</attributes>
     <GUI>
-      <xloc>288</xloc>
       <yloc>96</yloc>
+      <xloc>432</xloc>
     </GUI>
-  </transform>
-  <transform>
+    <copies>1</copies>
     <name>count==0?</name>
-    <type>FilterRows</type>
-    <description/>
     <distribute>Y</distribute>
-    <custom_distribution/>
-    <copies>1</copies>
-    <partitioning>
-      <method>none</method>
-      <schema_name/>
-    </partitioning>
+    <send_true_to>Nothing logged: abort</send_true_to>
     <compare>
       <condition>
+        <leftvalue>count</leftvalue>
+        <operator>-</operator>
+        <negated>N</negated>
         <conditions>
 </conditions>
         <function>=</function>
-        <leftvalue>count</leftvalue>
-        <negated>N</negated>
-        <operator>-</operator>
         <value>
-          <isnull>N</isnull>
-          <length>-1</length>
-          <mask>####0;-####0</mask>
           <name>constant</name>
-          <precision>0</precision>
+          <length>-1</length>
           <text>0</text>
+          <isnull>N</isnull>
           <type>Integer</type>
+          <mask>####0;-####0</mask>
+          <precision>0</precision>
         </value>
       </condition>
     </compare>
-    <send_true_to>Nothing logged: abort</send_true_to>
-    <attributes/>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+  </transform>
+  <transform>
+    <type>Abort</type>
+    <attributes>
+</attributes>
     <GUI>
-      <xloc>432</xloc>
       <yloc>96</yloc>
+      <xloc>608</xloc>
     </GUI>
-  </transform>
-  <transform>
+    <copies>1</copies>
     <name>Nothing logged: abort</name>
-    <type>Abort</type>
-    <description/>
     <distribute>Y</distribute>
-    <custom_distribution/>
-    <copies>1</copies>
+    <always_log_rows>Y</always_log_rows>
+    <message>No logging was performed on the remote server: no log folders 
detected.</message>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <row_threshold>0</row_threshold>
     <partitioning>
       <method>none</method>
       <schema_name/>
     </partitioning>
-    <abort_option>ABORT_WITH_ERROR</abort_option>
-    <always_log_rows>Y</always_log_rows>
-    <message>No logging was performed on the remote server: no log folders 
detected.</message>
-    <row_threshold>0</row_threshold>
-    <attributes/>
-    <GUI>
-      <xloc>608</xloc>
-      <yloc>96</yloc>
-    </GUI>
   </transform>
+  <notepads>
+</notepads>
+  <order>
+    <hop>
+      <to>count</to>
+      <from>list /tmp/executions</from>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <to>count==0?</to>
+      <from>count</from>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <to>Nothing logged: abort</to>
+      <from>count==0?</from>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <attributes>
+</attributes>
   <transform_error_handling>
-  </transform_error_handling>
-  <attributes/>
+</transform_error_handling>
 </pipeline>
diff --git 
a/integration-tests/mail/datasets/0008-email-messages-connection-golden.csv 
b/integration-tests/mail/datasets/0008-email-messages-connection-golden.csv
index 4074c518a1..b519d0ee70 100644
--- a/integration-tests/mail/datasets/0008-email-messages-connection-golden.csv
+++ b/integration-tests/mail/datasets/0008-email-messages-connection-golden.csv
@@ -1,2 +1,2 @@
 Message number,Subject,Sender,Reply to,Recipients,Description,Folder name,Flag 
new,Flag read,Flag flagged,Flag draft,Flag deleted,Attached files count,Mail 
Header(Name)
-1,ACTION: Appointment confirmation,"""\""Wile E. Coyote\"""" 
<[email protected]>","""\""Wile E. Coyote\"""" 
<[email protected]>",[email protected],,INBOX,Y,Y,N,N,N,0,
+1,ACTION: Appointment confirmation,"""\""Wile E. Coyote\"""" 
<[email protected]>","""\""Wile E. Coyote\"""" 
<[email protected]>",[email protected],,INBOX,N,Y,N,N,N,0,
diff --git 
a/integration-tests/parameters_and_variables/0009-getConnectionValues.hpl 
b/integration-tests/parameters_and_variables/0009-getConnectionValues.hpl
index dc6f1e80fd..e7f78ec5f7 100644
--- a/integration-tests/parameters_and_variables/0009-getConnectionValues.hpl
+++ b/integration-tests/parameters_and_variables/0009-getConnectionValues.hpl
@@ -199,7 +199,7 @@ com (for direct db connection)
   </transform>
   <transform>
     <name>Text file input</name>
-    <type>TextFileInput</type>
+    <type>TextFileInput2</type>
     <description/>
     <distribute>Y</distribute>
     <custom_distribution/>
diff --git a/integration-tests/parameters_and_variables/run-all.hwf 
b/integration-tests/parameters_and_variables/run-all.hwf
index 9af1eb41be..644e3efac1 100644
--- a/integration-tests/parameters_and_variables/run-all.hwf
+++ b/integration-tests/parameters_and_variables/run-all.hwf
@@ -35,18 +35,16 @@ limitations under the License.
       <description/>
       <type>SPECIAL</type>
       <attributes/>
-      <start>Y</start>
-      <dummy>N</dummy>
-      <repeat>N</repeat>
-      <schedulerType>0</schedulerType>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
       <intervalSeconds>0</intervalSeconds>
+      <schedulerType>0</schedulerType>
       <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
       <hour>12</hour>
-      <minutes>0</minutes>
+      <repeat>N</repeat>
       <weekDay>1</weekDay>
-      <DayOfMonth>1</DayOfMonth>
+      <minutes>0</minutes>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>80</xloc>
       <yloc>80</yloc>
       <attributes_hac/>
@@ -56,57 +54,47 @@ limitations under the License.
       <description/>
       <type>WORKFLOW</type>
       <attributes/>
-      <run_configuration>local</run_configuration>
-      
<filename>${PROJECT_HOME}/main-0001-workflow-set-get-variable.hwf</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <loglevel>Nothing</loglevel>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
-      <create_parent_folder>N</create_parent_folder>
-      <pass_export>N</pass_export>
-      <run_configuration>local</run_configuration>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <add_date>N</add_date>
+      <params_from_previous>N</params_from_previous>
+      <add_time>N</add_time>
+      <set_logfile>N</set_logfile>
+      <exec_per_row>N</exec_per_row>
+      
<filename>${PROJECT_HOME}/main-0001-workflow-set-get-variable.hwf</filename>
       <set_append_logfile>N</set_append_logfile>
+      <run_configuration>local</run_configuration>
+      <create_parent_folder>N</create_parent_folder>
+      <loglevel>Nothing</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>256</xloc>
       <yloc>80</yloc>
       <attributes_hac/>
     </action>
     <action>
-      <name>main-0002-pipeline-job-executor.hwf</name>
+      <name>main-0002-pipeline-workflow-executor.hwf</name>
       <description/>
       <type>WORKFLOW</type>
       <attributes/>
-      <run_configuration>local</run_configuration>
-      <filename>${PROJECT_HOME}/main-0002-pipeline-job-executor.hwf</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <set_logfile>N</set_logfile>
-      <logfile/>
       <logext/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <loglevel>Nothing</loglevel>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
-      <create_parent_folder>N</create_parent_folder>
-      <pass_export>N</pass_export>
-      <run_configuration>local</run_configuration>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <add_date>N</add_date>
+      <params_from_previous>N</params_from_previous>
+      <logfile/>
+      <add_time>N</add_time>
+      <set_logfile>N</set_logfile>
+      <exec_per_row>N</exec_per_row>
+      
<filename>${PROJECT_HOME}/main-0002-pipeline-workflow-executor.hwf</filename>
       <set_append_logfile>N</set_append_logfile>
+      <run_configuration>local</run_configuration>
+      <create_parent_folder>N</create_parent_folder>
+      <loglevel>Nothing</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>528</xloc>
       <yloc>80</yloc>
       <attributes_hac/>
@@ -116,27 +104,21 @@ limitations under the License.
       <description/>
       <type>WORKFLOW</type>
       <attributes/>
-      <run_configuration>local</run_configuration>
-      
<filename>${PROJECT_HOME}/main-0003-pipeline-pipeline-executor.hwf</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <loglevel>Nothing</loglevel>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
-      <create_parent_folder>N</create_parent_folder>
-      <pass_export>N</pass_export>
-      <run_configuration>local</run_configuration>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <add_date>N</add_date>
+      <params_from_previous>N</params_from_previous>
+      <add_time>N</add_time>
+      <set_logfile>N</set_logfile>
+      <exec_per_row>N</exec_per_row>
+      
<filename>${PROJECT_HOME}/main-0003-pipeline-pipeline-executor.hwf</filename>
       <set_append_logfile>N</set_append_logfile>
+      <run_configuration>local</run_configuration>
+      <create_parent_folder>N</create_parent_folder>
+      <loglevel>Nothing</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>816</xloc>
       <yloc>80</yloc>
       <attributes_hac/>
@@ -146,27 +128,21 @@ limitations under the License.
       <description/>
       <type>WORKFLOW</type>
       <attributes/>
-      <run_configuration>local</run_configuration>
-      
<filename>${PROJECT_HOME}/main-0004-pass-variables-and-parameters.hwf</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <loglevel>Nothing</loglevel>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
-      <create_parent_folder>N</create_parent_folder>
-      <pass_export>N</pass_export>
-      <run_configuration>local</run_configuration>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <add_date>N</add_date>
+      <params_from_previous>N</params_from_previous>
+      <add_time>N</add_time>
+      <set_logfile>N</set_logfile>
+      <exec_per_row>N</exec_per_row>
+      
<filename>${PROJECT_HOME}/main-0004-pass-variables-and-parameters.hwf</filename>
       <set_append_logfile>N</set_append_logfile>
+      <run_configuration>local</run_configuration>
+      <create_parent_folder>N</create_parent_folder>
+      <loglevel>Nothing</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>254</xloc>
       <yloc>194</yloc>
       <attributes_hac/>
@@ -176,27 +152,21 @@ limitations under the License.
       <description/>
       <type>WORKFLOW</type>
       <attributes/>
-      <run_configuration>local</run_configuration>
-      <filename>${PROJECT_HOME}/main-0005-mappings.hwf</filename>
-      <params_from_previous>N</params_from_previous>
-      <exec_per_row>N</exec_per_row>
-      <set_logfile>N</set_logfile>
-      <logfile/>
-      <logext/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <loglevel>Nothing</loglevel>
-      <wait_until_finished>Y</wait_until_finished>
-      <follow_abort_remote>N</follow_abort_remote>
-      <create_parent_folder>N</create_parent_folder>
-      <pass_export>N</pass_export>
-      <run_configuration>local</run_configuration>
       <parameters>
         <pass_all_parameters>Y</pass_all_parameters>
       </parameters>
+      <add_date>N</add_date>
+      <params_from_previous>N</params_from_previous>
+      <add_time>N</add_time>
+      <set_logfile>N</set_logfile>
+      <exec_per_row>N</exec_per_row>
+      <filename>${PROJECT_HOME}/main-0005-mappings.hwf</filename>
       <set_append_logfile>N</set_append_logfile>
+      <run_configuration>local</run_configuration>
+      <create_parent_folder>N</create_parent_folder>
+      <loglevel>Nothing</loglevel>
+      <wait_until_finished>Y</wait_until_finished>
       <parallel>N</parallel>
-      <nr>0</nr>
       <xloc>528</xloc>
       <yloc>192</yloc>
       <attributes_hac/>
@@ -206,26 +176,20 @@ limitations under the License.
     <hop>
       <from>START</from>
       <to>main-0001-workflow-set-get-variable.hwf</to>
-      <from_nr>0</from_nr>
-      <to_nr>0</to_nr>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>Y</unconditional>
     </hop>
     <hop>
       <from>main-0001-workflow-set-get-variable.hwf</from>
-      <to>main-0002-pipeline-job-executor.hwf</to>
-      <from_nr>0</from_nr>
-      <to_nr>0</to_nr>
+      <to>main-0002-pipeline-workflow-executor.hwf</to>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
     </hop>
     <hop>
-      <from>main-0002-pipeline-job-executor.hwf</from>
+      <from>main-0002-pipeline-workflow-executor.hwf</from>
       <to>main-0003-pipeline-pipeline-executor.hwf</to>
-      <from_nr>0</from_nr>
-      <to_nr>0</to_nr>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
@@ -233,8 +197,6 @@ limitations under the License.
     <hop>
       <from>main-0003-pipeline-pipeline-executor.hwf</from>
       <to>main-0004-pass-variables-and-parameters.hwf</to>
-      <from_nr>0</from_nr>
-      <to_nr>0</to_nr>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
@@ -242,8 +204,6 @@ limitations under the License.
     <hop>
       <from>main-0004-pass-variables-and-parameters.hwf</from>
       <to>main-0005-mappings.hwf</to>
-      <from_nr>0</from_nr>
-      <to_nr>0</to_nr>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
diff --git 
a/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByMeta.java
 
b/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByMeta.java
index 88da90aac8..74b7289123 100644
--- 
a/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByMeta.java
+++ 
b/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByMeta.java
@@ -42,7 +42,7 @@ import org.apache.hop.core.row.value.ValueMetaNone;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.HopMetadataProperty;
-import org.apache.hop.metadata.api.IEnumHasCode;
+import org.apache.hop.metadata.api.IEnumHasCodeAndDescription;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
@@ -323,7 +323,7 @@ public class MemoryGroupByMeta extends 
BaseTransformMeta<MemoryGroupBy, MemoryGr
   }
 
   @SuppressWarnings("java:S115")
-  public enum GroupType implements IEnumHasCode {
+  public enum GroupType implements IEnumHasCodeAndDescription {
     None("-", "-"),
     Sum("SUM", BaseMessages.getString(PKG, 
"MemoryGroupByMeta.TypeGroupLongDesc.SUM")),
     Average("AVERAGE", BaseMessages.getString(PKG, 
"MemoryGroupByMeta.TypeGroupLongDesc.AVERAGE")),
diff --git 
a/plugins/transforms/mongodb/src/main/java/org/apache/hop/mongo/wrapper/field/MongoField.java
 
b/plugins/transforms/mongodb/src/main/java/org/apache/hop/mongo/wrapper/field/MongoField.java
index 1ee077a39f..68621ded13 100644
--- 
a/plugins/transforms/mongodb/src/main/java/org/apache/hop/mongo/wrapper/field/MongoField.java
+++ 
b/plugins/transforms/mongodb/src/main/java/org/apache/hop/mongo/wrapper/field/MongoField.java
@@ -57,7 +57,7 @@ public class MongoField implements Comparable<MongoField> {
 
   /** User-defined indexed values for String types */
   @HopMetadataProperty(key = "indexed_vals", injectionKey = "FIELD_INDEXED")
-  public List<String> indexedValues;
+  public List<String> indexedValues = new ArrayList<>();
 
   /**
    * Temporary variable to hold the min:max array index info for fields 
determined when sampling
diff --git 
a/plugins/transforms/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodboutput/MongoDbOutputMeta.java
 
b/plugins/transforms/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodboutput/MongoDbOutputMeta.java
index a5883edc85..d3196b687b 100644
--- 
a/plugins/transforms/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodboutput/MongoDbOutputMeta.java
+++ 
b/plugins/transforms/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodboutput/MongoDbOutputMeta.java
@@ -24,9 +24,9 @@ import lombok.Getter;
 import lombok.Setter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hop.core.CheckResult;
+import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
-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;
@@ -284,23 +284,6 @@ public class MongoDbOutputMeta extends 
MongoDbMeta<MongoDbOutput, MongoDbOutputD
     batchInsertSize = "100";
   }
 
-  /**
-   * Added for backwards compatibility
-   *
-   * @param transformNode XML Node of the transform
-   * @param metadataProvider metadata provider
-   * @throws HopXmlException when unable to load from XML
-   */
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    super.loadXml(transformNode, metadataProvider);
-    String tempCollection = XmlHandler.getTagValue(transformNode, 
"mongo_collection");
-    if (!Utils.isEmpty(tempCollection)) {
-      setCollection(tempCollection);
-    }
-  }
-
   @Override
   public void check(
       List<ICheckResult> remarks,
@@ -370,4 +353,9 @@ public class MongoDbOutputMeta extends 
MongoDbMeta<MongoDbOutput, MongoDbOutputD
   public boolean supportsErrorHandling() {
     return true;
   }
+
+  @Override
+  public void convertLegacyXml(Node transformNode) {
+    collection = Const.NVL(collection, XmlHandler.getTagValue(transformNode, 
"mongo_collection"));
+  }
 }
diff --git 
a/plugins/transforms/mongodb/src/test/java/org/apache/hop/mongo/wrapper/MongoFieldTest.java
 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/mongo/wrapper/MongoFieldTest.java
index e2796cd281..5d64f8d135 100644
--- 
a/plugins/transforms/mongodb/src/test/java/org/apache/hop/mongo/wrapper/MongoFieldTest.java
+++ 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/mongo/wrapper/MongoFieldTest.java
@@ -19,6 +19,7 @@ package org.apache.hop.mongo.wrapper;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -71,6 +72,13 @@ class MongoFieldTest {
     assertEquals("parent.child", mongoField.getPath());
   }
 
+  @Test
+  void testIndexedValuesDefaultInitialized() {
+    MongoField mongoField = new MongoField();
+    assertNotNull(mongoField.indexedValues);
+    assertTrue(mongoField.indexedValues.isEmpty());
+  }
+
   // "Number", "String", "Date", "Boolean", "Integer", "BigNumber", 
"Serializable",
   // "Binary", "Timestamp", "Internet Address"
   @Test
diff --git 
a/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaInjectionTest.java
 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaInjectionTest.java
new file mode 100644
index 0000000000..a46790c6d9
--- /dev/null
+++ 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaInjectionTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.mongodbdelete;
+
+import org.apache.hop.core.injection.BaseMetadataInjectionTestJunit5;
+import org.apache.hop.core.logging.HopLogStore;
+import org.apache.hop.core.logging.ILogChannelFactory;
+import 
org.apache.hop.pipeline.transforms.mongodboutput.MongoDbOutputMetaInjectionTest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class MongoDbDeleteMetaInjectionTest extends 
BaseMetadataInjectionTestJunit5<MongoDbDeleteMeta> {
+  private ILogChannelFactory oldLogChannelInterfaceFactory;
+
+  @BeforeEach
+  void setup() throws Exception {
+    oldLogChannelInterfaceFactory = HopLogStore.getLogChannelFactory();
+    MongoDbOutputMetaInjectionTest.setHopLogFactoryWithMock();
+    setup(new MongoDbDeleteMeta());
+  }
+
+  @AfterEach
+  void tearDown() {
+    HopLogStore.setLogChannelFactory(oldLogChannelInterfaceFactory);
+  }
+
+  @Test
+  void test() throws Exception {
+    check("CONNECTION", () -> meta.getConnectionName());
+    check("COLLECTION", () -> meta.getCollection());
+    check("RETRIES", () -> meta.nbRetries);
+    check("WRITE_RETRIES", () -> meta.getWriteRetries());
+    check("RETRY_DELAY", () -> meta.getWriteRetryDelay());
+    check("USE_JSON_QUERY", () -> meta.isUseJsonQuery());
+    check("JSON_QUERY", () -> meta.getJsonQuery());
+    check("EXECUTE_FOR_EACH_ROW", () -> meta.isExecuteForEachIncomingRow());
+    check("INCOMING_FIELD_1", () -> 
meta.getMongoFields().get(0).incomingField1);
+    check("INCOMING_FIELD_2", () -> 
meta.getMongoFields().get(0).incomingField2);
+    check("DOC_PATH", () -> meta.getMongoFields().get(0).mongoDocPath);
+    check("COMPARATOR", () -> meta.getMongoFields().get(0).comparator);
+  }
+}
diff --git 
a/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaTest.java
 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaTest.java
new file mode 100644
index 0000000000..6c8a0053da
--- /dev/null
+++ 
b/plugins/transforms/mongodb/src/test/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteMetaTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.mongodbdelete;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.hop.core.Const;
+import org.apache.hop.core.encryption.Encr;
+import org.apache.hop.core.encryption.TwoWayPasswordEncoderPluginType;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.plugins.PluginRegistry;
+import org.apache.hop.core.util.EnvUtil;
+import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
+import 
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidatorFactory;
+import 
org.apache.hop.pipeline.transforms.loadsave.validator.ListLoadSaveValidator;
+import org.apache.hop.pipeline.transforms.loadsave.validator.ObjectValidator;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+class MongoDbDeleteMetaTest {
+
+  @BeforeAll
+  static void beforeClass() throws HopException {
+    
PluginRegistry.addPluginType(TwoWayPasswordEncoderPluginType.getInstance());
+    PluginRegistry.init();
+    String passwordEncoderPluginID =
+        
Const.NVL(EnvUtil.getSystemProperty(Const.HOP_PASSWORD_ENCODER_PLUGIN), "Hop");
+    Encr.init(passwordEncoderPluginID);
+  }
+
+  @Test
+  void testRoundTrips() throws HopException {
+    Map<String, String> getterMap = new HashMap<>();
+    getterMap.put("execute_for_each_row", "isExecuteForEachIncomingRow");
+    getterMap.put("fields", "getMongoFields");
+
+    Map<String, String> setterMap = new HashMap<>();
+    setterMap.put("execute_for_each_row", "setExecuteForEachIncomingRow");
+    setterMap.put("fields", "setMongoFields");
+
+    LoadSaveTester tester =
+        new LoadSaveTester(
+            MongoDbDeleteMeta.class,
+            Arrays.asList(
+                "collection",
+                "connectionName",
+                "nbRetries",
+                "writeRetries",
+                "writeRetryDelay",
+                "useJsonQuery",
+                "jsonQuery",
+                "executeForEachIncomingRow",
+                "mongoFields"),
+            getterMap,
+            setterMap);
+
+    IFieldLoadSaveValidatorFactory validatorFactory = 
tester.getFieldLoadSaveValidatorFactory();
+    validatorFactory.registerValidator(
+        validatorFactory.getName(List.class, MongoDbDeleteField.class),
+        new ListLoadSaveValidator<>(
+            new ObjectValidator<>(
+                validatorFactory,
+                MongoDbDeleteField.class,
+                Arrays.asList("incomingField1", "incomingField2", 
"mongoDocPath", "comparator"))));
+
+    tester.testXmlRoundTrip();
+  }
+}
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 a5d9b80c68..d07e686268 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
@@ -28,7 +28,6 @@ import org.apache.hop.core.ICheckResult;
 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.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
@@ -91,42 +90,6 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
     this.inputTransforms = new ArrayList<>();
   }
 
-  /**
-   * keep loadXml to load old style xml information for the transform
-   *
-   * @deprecated
-   * @param transformNode the XML node from the pipeline
-   * @param metadataProvider metadata provider to resolve things
-   * @throws HopXmlException when we can't read the XML correctly
-   */
-  @Override
-  @Deprecated(since = "2.17")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    super.loadXml(transformNode, metadataProvider);
-    // keep for backwards compatibility
-    readData(transformNode);
-  }
-
-  private void readData(Node transformNode) throws HopXmlException {
-    try {
-      String numInputStreamsStr = XmlHandler.getTagValue(transformNode, 
"number_input");
-
-      // Skip if number_input doesn't exist (null or empty)
-      if (numInputStreamsStr == null || numInputStreamsStr.trim().isEmpty()) {
-        return;
-      }
-
-      int nInputStreams = Integer.parseInt(numInputStreamsStr);
-      for (int i = 0; i < nInputStreams; i++) {
-        inputTransforms.add(XmlHandler.getTagValue(transformNode, "transform" 
+ i));
-      }
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, 
"MultiMergeJoinMeta.Exception.UnableToLoadTransformMeta"), e);
-    }
-  }
-
   @Override
   public void setDefault() {
     joinType = joinTypes[0];
@@ -354,11 +317,18 @@ public class MultiMergeJoinMeta extends 
BaseTransformMeta<MultiMergeJoin, MultiM
 
   @Override
   public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
+
     // Get the number of input transforms
     int numberOfInput = Const.toInt(XmlHandler.getTagValue(node, 
"number_input"), -1);
     if (numberOfInput < 0) {
       return;
     }
+    if (inputTransforms == null) {
+      inputTransforms = new ArrayList<>();
+    }
     inputTransforms.clear();
     for (int i = 0; i < numberOfInput; i++) {
       String inputTransform = XmlHandler.getTagValue(node, "transform" + i);
diff --git 
a/plugins/transforms/mysqlbulkloader/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderMeta.java
 
b/plugins/transforms/mysqlbulkloader/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderMeta.java
index 81e5bf775d..8e8c80dfcb 100644
--- 
a/plugins/transforms/mysqlbulkloader/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderMeta.java
+++ 
b/plugins/transforms/mysqlbulkloader/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderMeta.java
@@ -29,7 +29,6 @@ import org.apache.hop.core.database.Database;
 import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.exception.HopException;
 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.row.IValueMeta;
 import org.apache.hop.core.row.RowMeta;
@@ -118,32 +117,28 @@ public class MySqlBulkLoaderMeta extends 
BaseTransformMeta<MySqlBulkLoader, MySq
   @HopMetadataProperty(key = "bulk_size")
   private String bulkSize;
 
-  /**
-   * @deprecated Keep for backwards compatibility
-   * @param transformNode xml transform node
-   * @param metadataProvider metadata provider
-   * @throws HopXmlException thrown when unable to read XML
-   */
+  /** Added for backwards compatibility with older XML "mapping" blocks. */
   @Override
-  @Deprecated(since = "2.13")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    super.loadXml(transformNode, metadataProvider);
-    try {
-      int nrvalues = XmlHandler.countNodes(transformNode, "mapping");
-      for (int i = 0; i < nrvalues; i++) {
-        Node vnode = XmlHandler.getSubNodeByNr(transformNode, "mapping", i);
-        Field field = new Field();
-        field.setFieldStream(XmlHandler.getTagValue(vnode, "field_name"));
-        field.setFieldTable(XmlHandler.getTagValue(vnode, "stream_name"));
-        field.setFieldFormatType(XmlHandler.getTagValue(vnode, 
"field_format_ok"));
-        fields.add(field);
+  public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
+
+    if (fields == null) {
+      fields = new java.util.ArrayList<>();
+    }
+
+    int nrvalues = XmlHandler.countNodes(node, "mapping");
+    for (int i = 0; i < nrvalues; i++) {
+      Node vnode = XmlHandler.getSubNodeByNr(node, "mapping", i);
+      if (vnode == null) {
+        continue;
       }
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(
-              PKG, 
"MySqlBulkLoaderMeta.Exception.UnableToReadTransformInfoFromXML"),
-          e);
+      Field field = new Field();
+      field.setFieldStream(XmlHandler.getTagValue(vnode, "field_name"));
+      field.setFieldTable(XmlHandler.getTagValue(vnode, "stream_name"));
+      field.setFieldFormatType(XmlHandler.getTagValue(vnode, 
"field_format_ok"));
+      fields.add(field);
     }
   }
 
diff --git 
a/plugins/transforms/pipelineexecutor/src/main/java/org/apache/hop/pipeline/transforms/pipelineexecutor/PipelineExecutorMeta.java
 
b/plugins/transforms/pipelineexecutor/src/main/java/org/apache/hop/pipeline/transforms/pipelineexecutor/PipelineExecutorMeta.java
index 904b1df2e0..0cbafd2462 100644
--- 
a/plugins/transforms/pipelineexecutor/src/main/java/org/apache/hop/pipeline/transforms/pipelineexecutor/PipelineExecutorMeta.java
+++ 
b/plugins/transforms/pipelineexecutor/src/main/java/org/apache/hop/pipeline/transforms/pipelineexecutor/PipelineExecutorMeta.java
@@ -29,7 +29,6 @@ import org.apache.hop.core.annotations.Transform;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.exception.HopPluginException;
 import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.file.IHasFilename;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
@@ -235,31 +234,21 @@ public class PipelineExecutorMeta
     super(); // allocate BaseTransformMeta
   }
 
-  /**
-   * @deprecated Added for backwards compatibility for old parameter style
-   * @param transformNode Transform node XML
-   * @param metadataProvider Metadata provider
-   * @throws HopXmlException when unable to parse XML
-   */
+  /** Added for backwards compatibility with older parameter style XML. */
   @Override
-  @Deprecated(since = "2.13")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    super.loadXml(transformNode, metadataProvider);
-    try {
-      // Load inherit_all_vars
-      //
-      String value =
-          XmlHandler.getTagValue(
-              XmlHandler.getSubNode(transformNode, "parameters"), 
"inherit_all_vars");
-      if (value != null) {
-        setInheritingAllVariables("Y".equalsIgnoreCase(value));
-      }
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(
-              PKG, 
"PipelineExecutorMeta.Exception.ErrorLoadingPipelineExecutorDetailsFromXML"),
-          e);
+  public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
+
+    // Load inherit_all_vars from the old nested location under <parameters>
+    Node parametersNode = XmlHandler.getSubNode(node, "parameters");
+    if (parametersNode == null) {
+      return;
+    }
+    String value = XmlHandler.getTagValue(parametersNode, "inherit_all_vars");
+    if (value != null) {
+      setInheritingAllVariables("Y".equalsIgnoreCase(value));
     }
   }
 
diff --git 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/Script.java
 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/Script.java
index 8b42f39581..2c77677149 100644
--- 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/Script.java
+++ 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/Script.java
@@ -640,7 +640,11 @@ public class Script extends BaseTransform<ScriptMeta, 
ScriptData> implements ITr
   @Override
   public boolean processRow() throws HopException {
     Object[] r = getRow();
-    if (r == null && !first) {
+
+    // If this transform receives input, we stop if there are no more rows.
+    // If this transform doesn't receive input we probably want to generate 
rows.
+    //
+    if (r == null && (!first || data.hasPreviousTransforms)) {
       processEndOfStream();
       setOutputDone();
       return false;
@@ -698,6 +702,9 @@ public class Script extends BaseTransform<ScriptMeta, 
ScriptData> implements ITr
       return false;
     }
 
+    TransformMeta[] prevTransforms = 
getPipelineMeta().getPrevTransforms(getTransformMeta());
+    data.hasPreviousTransforms = (prevTransforms != null) && 
(prevTransforms.length > 0);
+
     // Add init code here.
     // Get the actual Scripts from our MetaData
     //
diff --git 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptData.java
 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptData.java
index 7cdf9007c0..cca9d06fa5 100644
--- 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptData.java
+++ 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptData.java
@@ -38,6 +38,7 @@ public class ScriptData extends BaseTransformData implements 
ITransformData {
 
   public IRowMeta outputRowMeta;
   public int[] replaceIndex;
+  public boolean hasPreviousTransforms;
 
   /** */
   public ScriptData() {
diff --git 
a/plugins/transforms/sqlfileoutput/src/main/java/org/apache/hop/pipeline/transforms/sqlfileoutput/SQLFileOutputMeta.java
 
b/plugins/transforms/sqlfileoutput/src/main/java/org/apache/hop/pipeline/transforms/sqlfileoutput/SQLFileOutputMeta.java
index 3cabdddcbf..ae6e0fb56c 100644
--- 
a/plugins/transforms/sqlfileoutput/src/main/java/org/apache/hop/pipeline/transforms/sqlfileoutput/SQLFileOutputMeta.java
+++ 
b/plugins/transforms/sqlfileoutput/src/main/java/org/apache/hop/pipeline/transforms/sqlfileoutput/SQLFileOutputMeta.java
@@ -34,7 +34,6 @@ import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.exception.HopDatabaseException;
 import org.apache.hop.core.exception.HopException;
 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.row.IValueMeta;
 import org.apache.hop.core.util.Utils;
@@ -100,23 +99,17 @@ public class SQLFileOutputMeta extends 
BaseTransformMeta<SQLFileOutput, SQLFileO
   @HopMetadataProperty(key = "StartNewLine")
   private boolean startNewLine;
 
-  /**
-   * @deprecated keep for backwards compatibility
-   * @param transformNode the XML of the transform node
-   * @param metadataProvider the metadata provider
-   * @throws HopXmlException when unable to parse the XML
-   */
+  /** Added for backwards compatibility with older XML using "extention". */
   @Override
-  @Deprecated(since = "2.13")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    try {
-      super.loadXml(transformNode, metadataProvider);
+  public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
 
-      // Rename from extention to extension
-      file.extension = XmlHandler.getTagValue(transformNode, "file", 
"extention");
-    } catch (Exception e) {
-      throw new HopXmlException("Unable to load transform info from XML", e);
+    // Rename from legacy "extention" to "extension", but don't overwrite 
modern XML values.
+    String legacyExtension = XmlHandler.getTagValue(node, "file", "extention");
+    if (file != null && !Utils.isEmpty(legacyExtension)) {
+      file.extension = legacyExtension;
     }
   }
 
diff --git 
a/plugins/transforms/workflowexecutor/src/main/java/org/apache/hop/pipeline/transforms/workflowexecutor/WorkflowExecutorMeta.java
 
b/plugins/transforms/workflowexecutor/src/main/java/org/apache/hop/pipeline/transforms/workflowexecutor/WorkflowExecutorMeta.java
index a1f2068307..c3dd047b72 100644
--- 
a/plugins/transforms/workflowexecutor/src/main/java/org/apache/hop/pipeline/transforms/workflowexecutor/WorkflowExecutorMeta.java
+++ 
b/plugins/transforms/workflowexecutor/src/main/java/org/apache/hop/pipeline/transforms/workflowexecutor/WorkflowExecutorMeta.java
@@ -31,7 +31,6 @@ import org.apache.hop.core.annotations.Transform;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.exception.HopPluginException;
 import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.file.IHasFilename;
 import org.apache.hop.core.logging.LogChannel;
 import org.apache.hop.core.row.IRowMeta;
@@ -231,33 +230,21 @@ public class WorkflowExecutorMeta
     super(); // allocate BaseTransformMeta
   }
 
-  /**
-   * @deprecated keep for backwards compatibility
-   * @param transformNode the XML of the transform node
-   * @param metadataProvider the metadata provider
-   * @throws HopXmlException when unable to parse the XML
-   */
+  /** Added for backwards compatibility with older parameter style XML. */
   @Override
-  @Deprecated(since = "2.13")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    try {
-      super.loadXml(transformNode, metadataProvider);
-
-      // Load inherit_all_vars
-      //
-      String value =
-          XmlHandler.getTagValue(
-              XmlHandler.getSubNode(transformNode, "parameters"), 
"inherit_all_vars");
-      if (value != null) {
-        setInheritingAllVariables("Y".equalsIgnoreCase(value));
-      }
+  public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
 
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(
-              PKG, 
"WorkflowExecutorMeta.Exception.ErrorLoadingJobExecutorDetailsFromXML"),
-          e);
+    // Load inherit_all_vars from the old nested location under <parameters>
+    Node parametersNode = XmlHandler.getSubNode(node, "parameters");
+    if (parametersNode == null) {
+      return;
+    }
+    String value = XmlHandler.getTagValue(parametersNode, "inherit_all_vars");
+    if (value != null) {
+      setInheritingAllVariables("Y".equalsIgnoreCase(value));
     }
   }
 
diff --git 
a/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogMeta.java
 
b/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogMeta.java
index 53ffae9f3e..32a13da9b3 100644
--- 
a/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogMeta.java
+++ 
b/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogMeta.java
@@ -25,7 +25,7 @@ import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
-import org.apache.hop.core.exception.HopXmlException;
+import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.variables.IVariables;
@@ -101,20 +101,14 @@ public class WriteToLogMeta extends 
BaseTransformMeta<WriteToLog, WriteToLogData
     return retval;
   }
 
-  /**
-   * Added for backwards compatibility
-   *
-   * @deprecated
-   * @param transformNode The XML node of the transform
-   * @param metadataProvider The metadata provided
-   * @throws HopXmlException When unable to read the XML
-   */
+  /** Added for backwards compatibility with older prefixed log level values. 
*/
   @Override
-  @Deprecated(since = "2.13")
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    super.loadXml(transformNode, metadataProvider);
-    String loglevel = XmlHandler.getTagValue(transformNode, "loglevel");
+  public void convertLegacyXml(Node node) throws HopException {
+    if (node == null) {
+      return;
+    }
+
+    String loglevel = XmlHandler.getTagValue(node, "loglevel");
     if (loglevel != null && loglevel.startsWith("log_level_")) {
       switch (loglevel) {
         case "log_level_nothing":
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
index 6f25060d9f..fd45b5d200 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/ExplorerPerspective.java
@@ -2901,17 +2901,30 @@ public class ExplorerPerspective implements 
IHopPerspective, TabClosable, IFileD
       // Sort by full path ascending
       Arrays.sort(children, Comparator.comparing(Object::toString));
 
+      String metadataFolder = 
hopGui.getVariables().getVariable(Const.HOP_METADATA_FOLDER);
+      String metadataFolderName = null;
+      if (!Utils.isEmpty(metadataFolder)) {
+        String resolvedMetadataFolder = 
hopGui.getVariables().resolve(metadataFolder);
+        if (!Utils.isEmpty(resolvedMetadataFolder)) {
+          String normalizedMetadataFolder = 
resolvedMetadataFolder.replace('\\', '/');
+          int lastSlashIndex = normalizedMetadataFolder.lastIndexOf('/');
+          metadataFolderName =
+              (lastSlashIndex >= 0)
+                  ? normalizedMetadataFolder.substring(lastSlashIndex + 1)
+                  : normalizedMetadataFolder;
+        }
+      }
+
       for (boolean folder : new boolean[] {true, false}) {
         for (FileObject child : children) {
 
           String childName = child.getName().getBaseName();
-          String metadataFolder = 
hopGui.getVariables().getVariable(Const.HOP_METADATA_FOLDER);
+          boolean isMetadataFolderMatch =
+              !Utils.isEmpty(metadataFolderName) && 
metadataFolderName.equals(childName);
 
           // Skip hidden files or folders
           if (!showingHiddenFiles
-              && (child.isHidden()
-                  || childName.startsWith(".")
-                  || child.toString().contains(metadataFolder))) {
+              && (child.isHidden() || childName.startsWith(".") || 
isMetadataFolderMatch)) {
             continue;
           }
 

Reply via email to