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 e3dfcc5075 issue #1945 (XML cleanup of Token Replacement, extra tests) 
(#6882)
e3dfcc5075 is described below

commit e3dfcc5075bc48f4a30cb17ca56df28ce32fff1d
Author: Matt Casters <[email protected]>
AuthorDate: Fri Mar 27 13:00:48 2026 +0100

    issue #1945 (XML cleanup of Token Replacement, extra tests) (#6882)
    
    * issue #1945 (XML cleanup of Token Replacement, extra tests)
    
    * issue #1945 (Checkstyle)
---
 .../transforms/0094-token-replacement-validate.hpl | 176 ++++++
 .../datasets/golden-token-replacement.csv          |   3 +
 .../transforms/main-0094-token-replacement.hwf     |  80 +++
 .../metadata/dataset/golden-token-replacement.json |  32 ++
 .../0094-token-replacement-validate UNIT.json      |  36 ++
 .../tokenreplacement/TokenReplacementDialog.java   | 265 ++++-----
 .../tokenreplacement/TokenReplacementField.java    |  84 +--
 .../tokenreplacement/TokenReplacementMeta.java     | 621 ++++++---------------
 .../tokenreplacement/TokenReplacementMetaTest.java |  34 ++
 .../src/test/resources/token-replacement.xml       |  60 ++
 10 files changed, 732 insertions(+), 659 deletions(-)

diff --git a/integration-tests/transforms/0094-token-replacement-validate.hpl 
b/integration-tests/transforms/0094-token-replacement-validate.hpl
new file mode 100644
index 0000000000..d8140ffce7
--- /dev/null
+++ b/integration-tests/transforms/0094-token-replacement-validate.hpl
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+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.
+
+-->
+<pipeline>
+  <info>
+    <name>0094-token-replacement-validate</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>
+    <created_user>-</created_user>
+    <created_date>2026/03/26 13:49:40.778</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/03/26 13:49:40.778</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>input</from>
+      <to>Token Replacement</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Token Replacement</from>
+      <to>Validate</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>input</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>Line with ${VARIABLE1} to replace.</item>
+        <item>Value1</item>
+      </line>
+      <line>
+        <item>Another with ${VARIABLE2} in it.</item>
+        <item>Value2</item>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <currency/>
+        <set_empty_string>N</set_empty_string>
+        <name>input</name>
+        <format/>
+        <group/>
+        <decimal/>
+        <type>String</type>
+      </field>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <currency/>
+        <set_empty_string>N</set_empty_string>
+        <name>tokenValue</name>
+        <format/>
+        <group/>
+        <decimal/>
+        <type>String</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>128</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Token Replacement</name>
+    <type>TokenReplacementPlugin</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <input_type>field</input_type>
+    <input_text/>
+    <input_field_name>input</input_field_name>
+    <input_filename/>
+    <input_filename_in_field>N</input_filename_in_field>
+    <input_filename_field/>
+    <add_input_filename_to_result>N</add_input_filename_to_result>
+    <output_type>Field</output_type>
+    <output_field_name>output</output_field_name>
+    <output_filename/>
+    <output_filename_in_field>N</output_filename_in_field>
+    <output_filename_field/>
+    <append_output_filename>N</append_output_filename>
+    <create_parent_folder>N</create_parent_folder>
+    <output_file_format>UNIX</output_file_format>
+    <output_file_encoding>UTF-8</output_file_encoding>
+    <output_split_every>0</output_split_every>
+    
<include_transform_nr_in_output_filename>N</include_transform_nr_in_output_filename>
+    <include_part_nr_in_output_filename>N</include_part_nr_in_output_filename>
+    <include_date_in_output_filename>N</include_date_in_output_filename>
+    <include_time_in_output_filename>N</include_time_in_output_filename>
+    
<specify_date_format_output_filename>N</specify_date_format_output_filename>
+    <date_format_output_filename/>
+    <add_output_filename_to_result>N</add_output_filename_to_result>
+    <token_start_string>${</token_start_string>
+    <token_end_string>}</token_end_string>
+    <fields>
+      <field>
+        <field_name>tokenValue</field_name>
+        <token_name>VARIABLE1</token_name>
+      </field>
+      <field>
+        <field_name>tokenValue</field_name>
+        <token_name>VARIABLE2</token_name>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>240</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Validate</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>368</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/datasets/golden-token-replacement.csv 
b/integration-tests/transforms/datasets/golden-token-replacement.csv
new file mode 100644
index 0000000000..0b9425cb14
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-token-replacement.csv
@@ -0,0 +1,3 @@
+input,tokenValue,output
+Line with ${VARIABLE1} to replace.,Value1,Line with Value1 to replace.
+Another with ${VARIABLE2} in it.,Value2,Another with Value2 in it.
diff --git a/integration-tests/transforms/main-0094-token-replacement.hwf 
b/integration-tests/transforms/main-0094-token-replacement.hwf
new file mode 100644
index 0000000000..e9aeb2482f
--- /dev/null
+++ b/integration-tests/transforms/main-0094-token-replacement.hwf
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+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.
+
+-->
+<workflow>
+  <name>main-0094-token-replacement</name>
+  <name_sync_with_filename>Y</name_sync_with_filename>
+  <description/>
+  <extended_description/>
+  <workflow_version/>
+  <created_user>-</created_user>
+  <created_date>2026/03/26 13:49:20.000</created_date>
+  <modified_user>-</modified_user>
+  <modified_date>2026/03/26 13:49:20.000</modified_date>
+  <parameters>
+    </parameters>
+  <actions>
+    <action>
+      <name>Start</name>
+      <description/>
+      <type>SPECIAL</type>
+      <attributes/>
+      <DayOfMonth>1</DayOfMonth>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+      <hour>12</hour>
+      <intervalMinutes>60</intervalMinutes>
+      <intervalSeconds>0</intervalSeconds>
+      <minutes>0</minutes>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
+      <weekDay>1</weekDay>
+      <parallel>N</parallel>
+      <xloc>50</xloc>
+      <yloc>50</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>0094-token-replacement-validate UNIT</name>
+      <description/>
+      <type>RunPipelineTests</type>
+      <attributes/>
+      <test_names>
+        <test_name>
+          <name>0094-token-replacement-validate UNIT</name>
+        </test_name>
+      </test_names>
+      <parallel>N</parallel>
+      <xloc>240</xloc>
+      <yloc>48</yloc>
+      <attributes_hac/>
+    </action>
+  </actions>
+  <hops>
+    <hop>
+      <from>Start</from>
+      <to>0094-token-replacement-validate UNIT</to>
+      <enabled>Y</enabled>
+      <evaluation>Y</evaluation>
+      <unconditional>Y</unconditional>
+    </hop>
+  </hops>
+  <notepads>
+  </notepads>
+  <attributes/>
+</workflow>
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-token-replacement.json 
b/integration-tests/transforms/metadata/dataset/golden-token-replacement.json
new file mode 100644
index 0000000000..7d7e0a3aab
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-token-replacement.json
@@ -0,0 +1,32 @@
+{
+  "base_filename": "golden-token-replacement.csv",
+  "name": "golden-token-replacement",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "input",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "tokenValue",
+      "field_format": ""
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "output",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/unit-test/0094-token-replacement-validate
 UNIT.json 
b/integration-tests/transforms/metadata/unit-test/0094-token-replacement-validate
 UNIT.json
new file mode 100644
index 0000000000..c204204dc9
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/unit-test/0094-token-replacement-validate
 UNIT.json 
@@ -0,0 +1,36 @@
+{
+  "database_replacements": [],
+  "autoOpening": true,
+  "description": "",
+  "persist_filename": "",
+  "test_type": "UNIT_TEST",
+  "variableValues": [],
+  "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+  "golden_data_sets": [
+    {
+      "field_mappings": [
+        {
+          "transform_field": "input",
+          "data_set_field": "input"
+        },
+        {
+          "transform_field": "tokenValue",
+          "data_set_field": "tokenValue"
+        },
+        {
+          "transform_field": "output",
+          "data_set_field": "output"
+        }
+      ],
+      "field_order": [
+        "input"
+      ],
+      "data_set_name": "golden-token-replacement",
+      "transform_name": "Validate"
+    }
+  ],
+  "input_data_sets": [],
+  "name": "0094-token-replacement-validate UNIT",
+  "trans_test_tweaks": [],
+  "pipeline_filename": "./0094-token-replacement-validate.hpl"
+}
\ No newline at end of file
diff --git 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
index 1a3abf42c2..865387a708 100644
--- 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
+++ 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
@@ -144,7 +144,7 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
 
   private TableView wFields;
 
-  private ColumnInfo[] colinf;
+  private ColumnInfo[] columnInfos;
 
   private final TokenReplacementMeta input;
 
@@ -175,9 +175,39 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     CTabFolder wTabFolder = new CTabFolder(shell, SWT.BORDER);
     PropsUi.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
 
-    // ////////////////////////
-    // START OF INPUT TAB///
-    // /
+    addInputTab(wTabFolder, lsMod);
+    addOutputTab(wTabFolder, lsMod);
+    addTokensTab(wTabFolder, lsMod);
+
+    FormData fdTabFolder = new FormData();
+    fdTabFolder.left = new FormAttachment(0, 0);
+    fdTabFolder.top = new FormAttachment(wSpacer, margin);
+    fdTabFolder.right = new FormAttachment(100, 0);
+    fdTabFolder.bottom = new FormAttachment(wOk, -margin);
+    wTabFolder.setLayoutData(fdTabFolder);
+
+    lsResize =
+        event -> {
+          Point size = shell.getSize();
+          wFields.setSize(size.x - 10, size.y - 50);
+          wFields.table.setSize(size.x - 10, size.y - 50);
+          wFields.redraw();
+        };
+    shell.addListener(SWT.Resize, lsResize);
+
+    wTabFolder.setSelection(0);
+
+    getData();
+    input.setChanged(changed);
+    updateInputType();
+    updateOutputType();
+    focusTransformName();
+    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+
+    return transformName;
+  }
+
+  private void addInputTab(CTabFolder wTabFolder, ModifyListener lsMod) {
     CTabItem wInputTab = new CTabItem(wTabFolder, SWT.NONE);
     wInputTab.setFont(GuiResource.getInstance().getFontDefault());
     wInputTab.setText(BaseMessages.getString(PKG, 
"TokenReplacementDialog.InputTab.TabTitle"));
@@ -343,6 +373,16 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     fdbInputFilename.right = new FormAttachment(100, 0);
     fdbInputFilename.top = new FormAttachment(0, 0);
     wbInputFilename.setLayoutData(fdbInputFilename);
+    wbInputFilename.addListener(
+        SWT.Selection,
+        e ->
+            BaseDialog.presentFileDialog(
+                shell,
+                wInputFilename,
+                variables,
+                new String[] {"*"},
+                new String[] {BaseMessages.getString(PKG, 
"System.FileType.AllFiles")},
+                true));
 
     wInputFilename = new TextVar(variables, gInputFile, SWT.SINGLE | SWT.LEFT 
| SWT.BORDER);
     PropsUi.setLook(wInputFilename);
@@ -352,6 +392,9 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     fdInputFilename.top = new FormAttachment(0, margin);
     fdInputFilename.right = new FormAttachment(wbInputFilename, -margin);
     wInputFilename.setLayoutData(fdInputFilename);
+    // Whenever something changes, set the tooltip to the expanded version:
+    wInputFilename.addModifyListener(
+        e -> 
wInputFilename.setToolTipText(variables.resolve(wInputFilename.getText())));
 
     // File name in field line
     //
@@ -461,10 +504,9 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
 
     wInputComp.layout();
     wInputTab.setControl(wInputComp);
+  }
 
-    // ////////////////////////
-    // START OF OUTPUT TAB///
-    // /
+  private void addOutputTab(CTabFolder wTabFolder, ModifyListener lsMod) {
     CTabItem wOutputTab = new CTabItem(wTabFolder, SWT.NONE);
     wOutputTab.setFont(GuiResource.getInstance().getFontDefault());
     wOutputTab.setText(BaseMessages.getString(PKG, 
"TokenReplacementDialog.OutputTab.TabTitle"));
@@ -578,6 +620,16 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     fdbOutputFilename.right = new FormAttachment(100, 0);
     fdbOutputFilename.top = new FormAttachment(0, 0);
     wbOutputFilename.setLayoutData(fdbOutputFilename);
+    wbOutputFilename.addListener(
+        SWT.Selection,
+        e ->
+            BaseDialog.presentFileDialog(
+                shell,
+                wOutputFilename,
+                variables,
+                new String[] {"*"},
+                new String[] {BaseMessages.getString(PKG, 
"System.FileType.AllFiles")},
+                true));
 
     wOutputFilename = new TextVar(variables, gOutputFile, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wOutputFilename);
@@ -587,6 +639,9 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     fdOutputFilename.top = new FormAttachment(0, margin);
     fdOutputFilename.right = new FormAttachment(wbOutputFilename, -margin);
     wOutputFilename.setLayoutData(fdOutputFilename);
+    // Whenever something changes, set the tooltip to the expanded version:
+    wOutputFilename.addModifyListener(
+        e -> 
wOutputFilename.setToolTipText(variables.resolve(wOutputFilename.getText())));
 
     // File name in field line
     //
@@ -996,13 +1051,9 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
 
     wOutputComp.layout();
     wOutputTab.setControl(wOutputComp);
+  }
 
-    // ///////////////////////////////////////////////////////////
-    // / END OF FILE TAB
-    // ///////////////////////////////////////////////////////////
-
-    // Token tab...
-    //
+  private void addTokensTab(CTabFolder wTabFolder, ModifyListener lsMod) {
     CTabItem wTokensTab = new CTabItem(wTabFolder, SWT.NONE);
     wTokensTab.setFont(GuiResource.getInstance().getFontDefault());
     wTokensTab.setText(BaseMessages.getString(PKG, 
"TokenReplacementDialog.TokensTab.TabTitle"));
@@ -1022,6 +1073,7 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     fdGet.right = new FormAttachment(50, -margin);
     fdGet.bottom = new FormAttachment(100, 0);
     wGet.setLayoutData(fdGet);
+    wGet.addListener(SWT.Selection, e -> get());
 
     // Token Start String
     Label wlTokenStartString = new Label(wTokensComp, SWT.RIGHT);
@@ -1064,29 +1116,27 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     wTokenEndString.setLayoutData(fdTokenEndString);
 
     // Tokens table
-    final int FieldsCols = 2;
-    final int FieldsRows = input.getTokenReplacementFields().length;
-
-    colinf = new ColumnInfo[FieldsCols];
-    colinf[0] =
-        new ColumnInfo(
-            BaseMessages.getString(PKG, 
"TokenReplacementDialog.TokenColumn.Column"),
-            ColumnInfo.COLUMN_TYPE_TEXT,
-            false);
-    colinf[1] =
-        new ColumnInfo(
-            BaseMessages.getString(PKG, 
"TokenReplacementDialog.StreamColumn.Column"),
-            ColumnInfo.COLUMN_TYPE_CCOMBO,
-            new String[] {""},
-            false);
+
+    columnInfos =
+        new ColumnInfo[] {
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"TokenReplacementDialog.TokenColumn.Column"),
+              ColumnInfo.COLUMN_TYPE_TEXT,
+              false),
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"TokenReplacementDialog.StreamColumn.Column"),
+              ColumnInfo.COLUMN_TYPE_CCOMBO,
+              new String[] {""},
+              false)
+        };
 
     wFields =
         new TableView(
             variables,
             wTokensComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI,
-            colinf,
-            FieldsRows,
+            columnInfos,
+            1,
             lsMod,
             props);
 
@@ -1128,66 +1178,6 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
 
     wTokensComp.layout();
     wTokensTab.setControl(wTokensComp);
-
-    FormData fdTabFolder = new FormData();
-    fdTabFolder.left = new FormAttachment(0, 0);
-    fdTabFolder.top = new FormAttachment(wSpacer, margin);
-    fdTabFolder.right = new FormAttachment(100, 0);
-    fdTabFolder.bottom = new FormAttachment(wOk, -margin);
-    wTabFolder.setLayoutData(fdTabFolder);
-
-    // Add listeners
-    wGet.addListener(SWT.Selection, e -> get());
-
-    // Whenever something changes, set the tooltip to the expanded version:
-    wInputFilename.addModifyListener(
-        e -> 
wInputFilename.setToolTipText(variables.resolve(wInputFilename.getText())));
-
-    // Whenever something changes, set the tooltip to the expanded version:
-    wOutputFilename.addModifyListener(
-        e -> 
wOutputFilename.setToolTipText(variables.resolve(wOutputFilename.getText())));
-
-    wbInputFilename.addListener(
-        SWT.Selection,
-        e ->
-            BaseDialog.presentFileDialog(
-                shell,
-                wInputFilename,
-                variables,
-                new String[] {"*"},
-                new String[] {BaseMessages.getString(PKG, 
"System.FileType.AllFiles")},
-                true));
-
-    wbOutputFilename.addListener(
-        SWT.Selection,
-        e ->
-            BaseDialog.presentFileDialog(
-                shell,
-                wOutputFilename,
-                variables,
-                new String[] {"*"},
-                new String[] {BaseMessages.getString(PKG, 
"System.FileType.AllFiles")},
-                true));
-
-    lsResize =
-        event -> {
-          Point size = shell.getSize();
-          wFields.setSize(size.x - 10, size.y - 50);
-          wFields.table.setSize(size.x - 10, size.y - 50);
-          wFields.redraw();
-        };
-    shell.addListener(SWT.Resize, lsResize);
-
-    wTabFolder.setSelection(0);
-
-    getData();
-    input.setChanged(changed);
-    updateInputType();
-    updateOutputType();
-    focusTransformName();
-    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
-
-    return transformName;
   }
 
   private void setEncodings() {
@@ -1357,7 +1347,7 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     // Something was changed in the row.
     //
     String[] fieldNames = ConstUi.sortFieldNames(inputFields);
-    colinf[1].setComboValues(fieldNames);
+    columnInfos[1].setComboValues(fieldNames);
   }
 
   /** Copy information from the meta-data input to the dialog fields. */
@@ -1392,74 +1382,57 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
     wTokenStartString.setText(Const.NVL(input.getTokenStartString(), ""));
     wTokenEndString.setText(Const.NVL(input.getTokenEndString(), ""));
 
-    logDebug("getting fields info...");
-
-    for (int i = 0; i < input.getTokenReplacementFields().length; i++) {
-      TokenReplacementField field = input.getTokenReplacementFields()[i];
-
-      TableItem item = wFields.table.getItem(i);
-      if (field.getName() != null) {
-        item.setText(2, field.getName());
-      }
-      if (field.getTokenName() != null) {
-        item.setText(1, field.getTokenName());
-      }
+    for (TokenReplacementField field : input.getTokenReplacementFields()) {
+      TableItem item = new TableItem(wFields.table, SWT.NONE);
+      item.setText(1, Const.NVL(field.getTokenName(), ""));
+      item.setText(2, Const.NVL(field.getName(), ""));
     }
-
-    wFields.optWidth(true);
+    wFields.optimizeTableView();
   }
 
   private void cancel() {
     transformName = null;
-
     input.setChanged(backupChanged);
-
     dispose();
   }
 
-  private void getInfo(TokenReplacementMeta tfoi) {
-    tfoi.setInputType(wInputType.getText());
-    tfoi.setInputText(wInputText.getText());
-    tfoi.setInputFieldName(wInputField.getText());
-    tfoi.setInputFileName(wInputFilename.getText());
-    tfoi.setInputFileNameInField(wInputFilenameInField.getSelection());
-    tfoi.setInputFileNameField(wInputFilenameField.getText());
-    tfoi.setAddInputFileNameToResult(wAddInputFilenameToResult.getSelection());
-
-    tfoi.setOutputType(wOutputType.getText());
-    tfoi.setOutputFieldName(wOutputField.getText());
-    tfoi.setOutputFileName(wOutputFilename.getText());
-    tfoi.setOutputFileNameInField(wOutputFilenameInField.getSelection());
-    tfoi.setOutputFileNameField(wOutputFilenameField.getText());
-    tfoi.setAppendOutputFileName(wAppendOutputFilename.getSelection());
-    tfoi.setCreateParentFolder(wCreateParentFolder.getSelection());
-    tfoi.setOutputFileFormat(
+  private void getInfo(TokenReplacementMeta meta) {
+    meta.setInputType(wInputType.getText());
+    meta.setInputText(wInputText.getText());
+    meta.setInputFieldName(wInputField.getText());
+    meta.setInputFileName(wInputFilename.getText());
+    meta.setInputFileNameInField(wInputFilenameInField.getSelection());
+    meta.setInputFileNameField(wInputFilenameField.getText());
+    meta.setAddInputFileNameToResult(wAddInputFilenameToResult.getSelection());
+
+    meta.setOutputType(wOutputType.getText());
+    meta.setOutputFieldName(wOutputField.getText());
+    meta.setOutputFileName(wOutputFilename.getText());
+    meta.setOutputFileNameInField(wOutputFilenameInField.getSelection());
+    meta.setOutputFileNameField(wOutputFilenameField.getText());
+    meta.setAppendOutputFileName(wAppendOutputFilename.getSelection());
+    meta.setCreateParentFolder(wCreateParentFolder.getSelection());
+    meta.setOutputFileFormat(
         
TokenReplacementMeta.formatMapperLineTerminator[wFormat.getSelectionIndex()]);
-    tfoi.setOutputFileEncoding(wOutputFileEncoding.getText());
-    tfoi.setSplitEvery(Const.toInt(wOutputSplitEvery.getText(), 0));
-    
tfoi.setIncludeTransformNrInOutputFileName(wIncludeTransformNrInFilename.getSelection());
-    
tfoi.setIncludePartNrInOutputFileName(wIncludePartNrInFilename.getSelection());
-    tfoi.setIncludeDateInOutputFileName(wIncludeDateInFilename.getSelection());
-    tfoi.setIncludeTimeInOutputFileName(wIncludeTimeInFilename.getSelection());
-    tfoi.setSpecifyDateFormatOutputFileName(wSpecifyDateFormat.getSelection());
-    tfoi.setDateFormatOutputFileName(wDateFormat.getText());
-    
tfoi.setAddOutputFileNameToResult(wAddOutputFilenameToResult.getSelection());
-
-    tfoi.setTokenStartString(wTokenStartString.getText());
-    tfoi.setTokenEndString(wTokenEndString.getText());
-
-    int nrFields = wFields.nrNonEmpty();
-
-    tfoi.allocate(nrFields);
-
-    for (int i = 0; i < nrFields; i++) {
+    meta.setOutputFileEncoding(wOutputFileEncoding.getText());
+    meta.setSplitEvery(Const.toInt(wOutputSplitEvery.getText(), 0));
+    
meta.setIncludeTransformNrInOutputFileName(wIncludeTransformNrInFilename.getSelection());
+    
meta.setIncludePartNrInOutputFileName(wIncludePartNrInFilename.getSelection());
+    meta.setIncludeDateInOutputFileName(wIncludeDateInFilename.getSelection());
+    meta.setIncludeTimeInOutputFileName(wIncludeTimeInFilename.getSelection());
+    meta.setSpecifyDateFormatOutputFileName(wSpecifyDateFormat.getSelection());
+    meta.setDateFormatOutputFileName(wDateFormat.getText());
+    
meta.setAddOutputFileNameToResult(wAddOutputFilenameToResult.getSelection());
+    meta.setTokenStartString(wTokenStartString.getText());
+    meta.setTokenEndString(wTokenEndString.getText());
+
+    meta.getTokenReplacementFields().clear();
+    for (TableItem item : wFields.getNonEmptyItems()) {
       TokenReplacementField field = new TokenReplacementField();
+      meta.getTokenReplacementFields().add(field);
 
-      TableItem item = wFields.getNonEmpty(i);
-      field.setName(item.getText(2));
       field.setTokenName(item.getText(1));
-
-      tfoi.getTokenReplacementFields()[i] = field;
+      field.setName(item.getText(2));
     }
   }
 
diff --git 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementField.java
 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementField.java
index 70debe02f6..cdcd9e0c34 100644
--- 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementField.java
+++ 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementField.java
@@ -17,77 +17,51 @@
 
 package org.apache.hop.pipeline.transforms.tokenreplacement;
 
-import org.apache.hop.core.Const;
-import org.apache.hop.core.injection.Injection;
+import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 
 /** Describes a single field in a text file */
+@Getter
+@Setter
 public class TokenReplacementField implements Cloneable {
-  @Injection(name = "TOKEN_FIELDNAME", group = "OUTPUT_FIELDS")
+  @HopMetadataProperty(
+      key = "field_name",
+      injectionKey = "TOKEN_FIELDNAME",
+      injectionKeyDescription = "TokenReplacement.Injection.TOKEN_FIELDNAME")
   private String name;
 
-  @Injection(name = "TOKEN_NAME", group = "OUTPUT_FIELDS")
+  @HopMetadataProperty(
+      key = "token_name",
+      injectionKey = "TOKEN_NAME",
+      injectionKeyDescription = "TokenReplacement.Injection.TOKEN_NAME")
   private String tokenName;
 
-  public TokenReplacementField(String name, String tokenName) {
-    this.name = name;
-    this.tokenName = tokenName;
-  }
-
   public TokenReplacementField() {}
 
-  public boolean equals(Object obj) {
-    if (obj instanceof TokenReplacementField field) {
-      return name.equals(field.getName()) && tokenName.equals(field.tokenName);
-    }
-    return false;
+  public TokenReplacementField(TokenReplacementField f) {
+    this();
+    this.name = f.name;
+    this.tokenName = f.tokenName;
   }
 
   @Override
-  public Object clone() {
-    try {
-      return super.clone();
-    } catch (CloneNotSupportedException e) {
-      return null;
+  public boolean equals(Object o) {
+    if (o == null || getClass() != o.getClass()) {
+      return false;
     }
+    TokenReplacementField that = (TokenReplacementField) o;
+    return Objects.equals(name, that.name) && Objects.equals(tokenName, 
that.tokenName);
   }
 
-  /**
-   * Get the stream field name.
-   *
-   * @return name
-   */
-  public String getName() {
-    return name;
-  }
-
-  /**
-   * Set the stream field name.
-   *
-   * @param fieldname the name of the field
-   */
-  public void setName(String fieldname) {
-    this.name = fieldname;
-  }
-
-  /**
-   * Get the name of the token If the token name is null returns the stream 
field name
-   *
-   * @return tokenName
-   */
-  public String getTokenName() {
-    return Const.NVL(tokenName, name);
-  }
-
-  /**
-   * Set the name of the token
-   *
-   * @param tokenName the name of the token
-   */
-  public void setTokenName(String tokenName) {
-    this.tokenName = tokenName;
+  @Override
+  public int hashCode() {
+    return Objects.hash(name, tokenName);
   }
 
-  public String toString() {
-    return name;
+  @Override
+  public TokenReplacementField clone() {
+    return new TokenReplacementField(this);
   }
 }
diff --git 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMeta.java
 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMeta.java
index 12dc031d8d..b69c26fef6 100644
--- 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMeta.java
+++ 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMeta.java
@@ -18,29 +18,26 @@
 package org.apache.hop.pipeline.transforms.tokenreplacement;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 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.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
-import org.apache.hop.core.injection.Injection;
-import org.apache.hop.core.injection.InjectionDeep;
-import org.apache.hop.core.injection.InjectionSupported;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.w3c.dom.Node;
 
 @Transform(
     id = "TokenReplacementPlugin",
@@ -50,9 +47,8 @@ import org.w3c.dom.Node;
     categoryDescription = 
"i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Transform",
     keywords = "i18n::TokenReplacementMeta.keyword",
     documentationUrl = "/pipeline/transforms/tokenreplacement.html")
-@InjectionSupported(
-    localizationPrefix = "TokenReplacement.Injection.",
-    groups = {"OUTPUT_FIELDS"})
+@Getter
+@Setter
 public class TokenReplacementMeta
     extends BaseTransformMeta<TokenReplacement, TokenReplacementData> {
   private static final Class<?> PKG = TokenReplacementMeta.class;
@@ -105,193 +101,210 @@ public class TokenReplacementMeta
   public static final String CONST_FILE_ENCODING = "file.encoding";
   public static final String CONST_UTF_8 = "UTF-8";
 
-  @Injection(name = "INPUT_TYPE")
+  @HopMetadataProperty(
+      key = "input_type",
+      injectionKey = "INPUT_TYPE",
+      injectionKeyDescription = "TokenReplacement.Injection.INPUT_TYPE")
   private String inputType;
 
-  @Injection(name = "INPUT_TEXT")
+  @HopMetadataProperty(
+      key = "input_text",
+      injectionKey = "INPUT_TEXT",
+      injectionKeyDescription = "TokenReplacement.Injection.INPUT_TEXT")
   private String inputText;
 
-  @Injection(name = "INPUT_FIELD")
+  @HopMetadataProperty(
+      key = "input_field_name",
+      injectionKey = "INPUT_FIELD",
+      injectionKeyDescription = "TokenReplacement.Injection.INPUT_FIELD")
   private String inputFieldName;
 
-  @Injection(name = "INPUT_FILENAME")
+  @HopMetadataProperty(
+      key = "input_filename",
+      injectionKey = "INPUT_FILENAME",
+      injectionKeyDescription = "TokenReplacement.Injection.INPUT_FILENAME")
   private String inputFileName;
 
-  @Injection(name = "INPUT_FILENAME_IN_FIELD")
+  @HopMetadataProperty(
+      key = "input_filename_in_field",
+      injectionKey = "INPUT_FILENAME_IN_FIELD",
+      injectionKeyDescription = 
"TokenReplacement.Injection.INPUT_FILENAME_IN_FIELD")
   private boolean inputFileNameInField;
 
-  @Injection(name = "INPUT_FILENAME_FIELD")
+  @HopMetadataProperty(
+      key = "input_filename_field",
+      injectionKey = "INPUT_FILENAME_FIELD",
+      injectionKeyDescription = 
"TokenReplacement.Injection.INPUT_FILENAME_FIELD")
   private String inputFileNameField;
 
-  @Injection(name = "ADD_INPUT_FILENAME_TO_RESULT")
+  @HopMetadataProperty(
+      key = "add_input_filename_to_result",
+      injectionKey = "ADD_INPUT_FILENAME_TO_RESULT",
+      injectionKeyDescription = 
"TokenReplacement.Injection.ADD_INPUT_FILENAME_TO_RESULT")
   private boolean addInputFileNameToResult;
 
-  @Injection(name = "OUTPUT_TYPE")
+  @HopMetadataProperty(
+      key = "output_type",
+      injectionKey = "OUTPUT_TYPE",
+      injectionKeyDescription = "TokenReplacement.Injection.OUTPUT_TYPE")
   private String outputType;
 
-  @Injection(name = "OUTPUT_FIELD")
+  @HopMetadataProperty(
+      key = "output_field_name",
+      injectionKey = "OUTPUT_FIELD",
+      injectionKeyDescription = "TokenReplacement.Injection.OUTPUT_FIELD")
   private String outputFieldName;
 
-  @Injection(name = "OUTPUT_FILENAME")
+  @HopMetadataProperty(
+      key = "output_filename",
+      injectionKey = "OUTPUT_FILENAME",
+      injectionKeyDescription = "TokenReplacement.Injection.OUTPUT_FILENAME")
   private String outputFileName;
 
-  @Injection(name = "OUTPUT_FILENAME_IN_FIELD")
+  @HopMetadataProperty(
+      key = "output_filename_in_field",
+      injectionKey = "OUTPUT_FILENAME_IN_FIELD",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_FILENAME_IN_FIELD")
   private boolean outputFileNameInField;
 
-  @Injection(name = "OUTPUT_FILENAME_FIELD")
+  @HopMetadataProperty(
+      key = "output_filename_field",
+      injectionKey = "OUTPUT_FILENAME_FIELD",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_FILENAME_FIELD")
   private String outputFileNameField;
 
-  @Injection(name = "APPEND_OUTPUT_FILE")
+  @HopMetadataProperty(
+      key = "append_output_filename",
+      injectionKey = "APPEND_OUTPUT_FILE",
+      injectionKeyDescription = 
"TokenReplacement.Injection.APPEND_OUTPUT_FILE")
   private boolean appendOutputFileName;
 
-  @Injection(name = "CREATE_PARENT_FOLDER")
+  @HopMetadataProperty(
+      key = "create_parent_folder",
+      injectionKey = "CREATE_PARENT_FOLDER",
+      injectionKeyDescription = 
"TokenReplacement.Injection.CREATE_PARENT_FOLDER")
   private boolean createParentFolder;
 
-  @Injection(name = "OUTPUT_FORMAT")
+  @HopMetadataProperty(
+      key = "output_file_format",
+      injectionKey = "OUTPUT_FORMAT",
+      injectionKeyDescription = "TokenReplacement.Injection.OUTPUT_FORMAT")
   private String outputFileFormat;
 
-  @Injection(name = "OUTPUT_ENCODING")
+  @HopMetadataProperty(
+      key = "output_file_encoding",
+      injectionKey = "OUTPUT_ENCODING",
+      injectionKeyDescription = "TokenReplacement.Injection.OUTPUT_ENCODING")
   private String outputFileEncoding;
 
-  @Injection(name = "OUTPUT_SPLIT_EVERY")
+  @HopMetadataProperty(
+      key = "output_split_every",
+      injectionKey = "OUTPUT_SPLIT_EVERY",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_SPLIT_EVERY")
   private int splitEvery;
 
-  @Injection(name = "OUTPUT_INCLUDE_TRANSFORMNR")
+  @HopMetadataProperty(
+      key = "include_transform_nr_in_output_filename",
+      injectionKey = "OUTPUT_INCLUDE_TRANSFORMNR",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_INCLUDE_TRANSFORMNR")
   private boolean includeTransformNrInOutputFileName;
 
-  @Injection(name = "OUTPUT_INCLUDE_PARTNR")
+  @HopMetadataProperty(
+      key = "include_part_nr_in_output_filename",
+      injectionKey = "OUTPUT_INCLUDE_PARTNR",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_INCLUDE_PARTNR")
   private boolean includePartNrInOutputFileName;
 
-  @Injection(name = "OUTPUT_INCLUDE_DATE")
+  @HopMetadataProperty(
+      key = "include_date_in_output_filename",
+      injectionKey = "OUTPUT_INCLUDE_DATE",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_INCLUDE_DATE")
   private boolean includeDateInOutputFileName;
 
-  @Injection(name = "OUTPUT_INCLUDE_TIME")
+  @HopMetadataProperty(
+      key = "include_time_in_output_filename",
+      injectionKey = "OUTPUT_INCLUDE_TIME",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_INCLUDE_TIME")
   private boolean includeTimeInOutputFileName;
 
-  @Injection(name = "OUTPUT_SPECIFY_DATE_FORMAT")
+  @HopMetadataProperty(
+      key = "specify_date_format_output_filename",
+      injectionKey = "OUTPUT_SPECIFY_DATE_FORMAT",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_SPECIFY_DATE_FORMAT")
   private boolean specifyDateFormatOutputFileName;
 
-  @Injection(name = "OUTPUT_DATE_FORMAT")
+  @HopMetadataProperty(
+      key = "date_format_output_filename",
+      injectionKey = "OUTPUT_DATE_FORMAT",
+      injectionKeyDescription = 
"TokenReplacement.Injection.OUTPUT_DATE_FORMAT")
   private String dateFormatOutputFileName;
 
-  @Injection(name = "ADD_OUTPUT_FILENAME_TO_RESULT")
+  @HopMetadataProperty(
+      key = "add_output_filename_to_result",
+      injectionKey = "ADD_OUTPUT_FILENAME_TO_RESULT",
+      injectionKeyDescription = 
"TokenReplacement.Injection.ADD_OUTPUT_FILENAME_TO_RESULT")
   private boolean addOutputFileNameToResult;
 
-  @Injection(name = "TOKEN_START_STRING")
+  @HopMetadataProperty(
+      key = "token_start_string",
+      injectionKey = "TOKEN_START_STRING",
+      injectionKeyDescription = 
"TokenReplacement.Injection.TOKEN_START_STRING")
   private String tokenStartString;
 
-  @Injection(name = "TOKEN_END_STRING")
+  @HopMetadataProperty(
+      key = "token_end_string",
+      injectionKey = "TOKEN_END_STRING",
+      injectionKeyDescription = "TokenReplacement.Injection.TOKEN_END_STRING")
   private String tokenEndString;
 
-  @InjectionDeep private TokenReplacementField[] tokenReplacementFields;
+  @HopMetadataProperty(
+      key = "field",
+      groupKey = "fields",
+      injectionGroupKey = "OUTPUT_FIELDS",
+      injectionGroupDescription = "TokenReplacement.Injection.OUTPUT_FIELDS")
+  private List<TokenReplacementField> tokenReplacementFields;
 
   public TokenReplacementMeta() {
-    super(); // allocate BaseTransformMeta
-    allocate(0);
-  }
-
-  public String getInputType() {
-    return inputType;
-  }
-
-  public void setInputType(String inputType) {
-    this.inputType = inputType;
-  }
-
-  public String getInputText() {
-    return inputText;
-  }
-
-  public void setInputText(String inputText) {
-    this.inputText = inputText;
-  }
-
-  public String getInputFieldName() {
-    return inputFieldName;
-  }
-
-  public void setInputFieldName(String inputFieldName) {
-    this.inputFieldName = inputFieldName;
-  }
-
-  public String getInputFileName() {
-    return inputFileName;
-  }
-
-  public void setInputFileName(String inputFileName) {
-    this.inputFileName = inputFileName;
-  }
-
-  public boolean isInputFileNameInField() {
-    return inputFileNameInField;
-  }
-
-  public void setInputFileNameInField(boolean inputFileNameInField) {
-    this.inputFileNameInField = inputFileNameInField;
-  }
-
-  public String getInputFileNameField() {
-    return inputFileNameField;
-  }
-
-  public void setInputFileNameField(String inputFileNameField) {
-    this.inputFileNameField = inputFileNameField;
-  }
-
-  public boolean isAddInputFileNameToResult() {
-    return addInputFileNameToResult;
-  }
-
-  public void setAddInputFileNameToResult(boolean addInputFileNameToResult) {
-    this.addInputFileNameToResult = addInputFileNameToResult;
-  }
-
-  public String getOutputType() {
-    return outputType;
-  }
-
-  public void setOutputType(String outputType) {
-    this.outputType = outputType;
-  }
-
-  public String getOutputFieldName() {
-    return outputFieldName;
-  }
-
-  public void setOutputFieldName(String outputFieldName) {
-    this.outputFieldName = outputFieldName;
-  }
-
-  public String getOutputFileName() {
-    return outputFileName;
-  }
-
-  public void setOutputFileName(String outputFileName) {
-    this.outputFileName = outputFileName;
-  }
-
-  public boolean isOutputFileNameInField() {
-    return outputFileNameInField;
-  }
-
-  public void setOutputFileNameInField(boolean outputFileNameInField) {
-    this.outputFileNameInField = outputFileNameInField;
-  }
-
-  public String getOutputFileNameField() {
-    return outputFileNameField;
-  }
-
-  public void setOutputFileNameField(String outputFileNameField) {
-    this.outputFileNameField = outputFileNameField;
-  }
-
-  public boolean isCreateParentFolder() {
-    return createParentFolder;
+    super();
+    tokenReplacementFields = new ArrayList<>();
+    inputType = "Text";
+    outputType = "Field";
+    outputFileEncoding = Const.getEnvironmentVariable(CONST_FILE_ENCODING, 
CONST_UTF_8);
+    outputFileFormat = Const.isWindows() ? "DOS" : "UNIX";
+    splitEvery = 0;
+    tokenStartString = "${";
+    tokenEndString = "}";
   }
 
-  public void setCreateParentFolder(boolean createParentFolder) {
-    this.createParentFolder = createParentFolder;
+  public TokenReplacementMeta(TokenReplacementMeta m) {
+    this();
+    this.inputType = m.inputType;
+    this.inputText = m.inputText;
+    this.inputFieldName = m.inputFieldName;
+    this.inputFileName = m.inputFileName;
+    this.inputFileNameInField = m.inputFileNameInField;
+    this.inputFileNameField = m.inputFileNameField;
+    this.addInputFileNameToResult = m.addInputFileNameToResult;
+    this.outputType = m.outputType;
+    this.outputFieldName = m.outputFieldName;
+    this.outputFileName = m.outputFileName;
+    this.outputFileNameInField = m.outputFileNameInField;
+    this.outputFileNameField = m.outputFileNameField;
+    this.appendOutputFileName = m.appendOutputFileName;
+    this.createParentFolder = m.createParentFolder;
+    this.outputFileFormat = m.outputFileFormat;
+    this.outputFileEncoding = m.outputFileEncoding;
+    this.splitEvery = m.splitEvery;
+    this.includeTransformNrInOutputFileName = 
m.includeTransformNrInOutputFileName;
+    this.includePartNrInOutputFileName = m.includePartNrInOutputFileName;
+    this.includeDateInOutputFileName = m.includeDateInOutputFileName;
+    this.includeTimeInOutputFileName = m.includeTimeInOutputFileName;
+    this.specifyDateFormatOutputFileName = m.specifyDateFormatOutputFileName;
+    this.dateFormatOutputFileName = m.dateFormatOutputFileName;
+    this.addOutputFileNameToResult = m.addOutputFileNameToResult;
+    this.tokenStartString = m.tokenStartString;
+    this.tokenEndString = m.tokenEndString;
+    m.tokenReplacementFields.forEach(f -> tokenReplacementFields.add(new 
TokenReplacementField(f)));
   }
 
   public String getOutputFileEncoding() {
@@ -299,14 +312,6 @@ public class TokenReplacementMeta
         outputFileEncoding, Const.getEnvironmentVariable(CONST_FILE_ENCODING, 
CONST_UTF_8));
   }
 
-  public void setOutputFileEncoding(String outputFileEncoding) {
-    this.outputFileEncoding = outputFileEncoding;
-  }
-
-  public String getOutputFileFormat() {
-    return outputFileFormat;
-  }
-
   public String getOutputFileFormatString() {
     return switch (outputFileFormat) {
       case "DOS" -> "\r\n";
@@ -316,308 +321,9 @@ public class TokenReplacementMeta
     };
   }
 
-  public void setOutputFileFormat(String outputFileFormat) {
-    this.outputFileFormat = outputFileFormat;
-  }
-
-  public int getSplitEvery() {
-    return splitEvery;
-  }
-
-  public void setSplitEvery(int splitEvery) {
-    this.splitEvery = splitEvery;
-  }
-
-  public boolean isIncludeTransformNrInOutputFileName() {
-    return includeTransformNrInOutputFileName;
-  }
-
-  public void setIncludeTransformNrInOutputFileName(boolean 
includeTransformNrInOutputFileName) {
-    this.includeTransformNrInOutputFileName = 
includeTransformNrInOutputFileName;
-  }
-
-  public boolean isIncludePartNrInOutputFileName() {
-    return includePartNrInOutputFileName;
-  }
-
-  public void setIncludePartNrInOutputFileName(boolean 
includePartNrInOutputFileName) {
-    this.includePartNrInOutputFileName = includePartNrInOutputFileName;
-  }
-
-  public boolean isIncludeDateInOutputFileName() {
-    return includeDateInOutputFileName;
-  }
-
-  public void setIncludeDateInOutputFileName(boolean 
includeDateInOutputFileName) {
-    this.includeDateInOutputFileName = includeDateInOutputFileName;
-  }
-
-  public boolean isIncludeTimeInOutputFileName() {
-    return includeTimeInOutputFileName;
-  }
-
-  public void setIncludeTimeInOutputFileName(boolean 
includeTimeInOutputFileName) {
-    this.includeTimeInOutputFileName = includeTimeInOutputFileName;
-  }
-
-  public boolean isSpecifyDateFormatOutputFileName() {
-    return specifyDateFormatOutputFileName;
-  }
-
-  public void setSpecifyDateFormatOutputFileName(boolean 
specifyDateFormatOutputFileName) {
-    this.specifyDateFormatOutputFileName = specifyDateFormatOutputFileName;
-  }
-
-  public String getDateFormatOutputFileName() {
-    return dateFormatOutputFileName;
-  }
-
-  public void setDateFormatOutputFileName(String dateFormatOutputFileName) {
-    this.dateFormatOutputFileName = dateFormatOutputFileName;
-  }
-
-  public boolean isAddOutputFileNameToResult() {
-    return addOutputFileNameToResult;
-  }
-
-  public void setAddOutputFileNameToResult(boolean addOutputFileNameToResult) {
-    this.addOutputFileNameToResult = addOutputFileNameToResult;
-  }
-
-  public String getTokenStartString() {
-    return tokenStartString;
-  }
-
-  public void setTokenStartString(String tokenStartString) {
-    this.tokenStartString = tokenStartString;
-  }
-
-  public String getTokenEndString() {
-    return tokenEndString;
-  }
-
-  public void setTokenEndString(String tokenEndString) {
-    this.tokenEndString = tokenEndString;
-  }
-
-  public TokenReplacementField[] getTokenReplacementFields() {
-    return tokenReplacementFields;
-  }
-
-  public void setTokenReplacementFields(TokenReplacementField[] 
tokenReplacementFields) {
-    this.tokenReplacementFields = tokenReplacementFields;
-  }
-
-  public boolean isAppendOutputFileName() {
-    return appendOutputFileName;
-  }
-
-  public void setAppendOutputFileName(boolean appendOutputFileName) {
-    this.appendOutputFileName = appendOutputFileName;
-  }
-
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider 
metadataProvider)
-      throws HopXmlException {
-    try {
-
-      inputType = XmlHandler.getTagValue(transformNode, INPUT_TYPE);
-      inputText = XmlHandler.getTagValue(transformNode, INPUT_TEXT);
-      inputFieldName = XmlHandler.getTagValue(transformNode, INPUT_FIELD_NAME);
-      inputFileName = XmlHandler.getTagValue(transformNode, INPUT_FILENAME);
-      inputFileNameInField =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(XmlHandler.getTagValue(transformNode, 
INPUT_FILENAME_IN_FIELD), ""));
-      inputFileNameField = XmlHandler.getTagValue(transformNode, 
INPUT_FILENAME_FIELD);
-      addInputFileNameToResult =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
ADD_INPUT_FILENAME_TO_RESULT), ""));
-
-      outputType = XmlHandler.getTagValue(transformNode, OUTPUT_TYPE);
-      outputFieldName = XmlHandler.getTagValue(transformNode, 
OUTPUT_FIELD_NAME);
-      outputFileName = XmlHandler.getTagValue(transformNode, OUTPUT_FILENAME);
-      outputFileNameInField =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(XmlHandler.getTagValue(transformNode, 
OUTPUT_FILENAME_IN_FIELD), ""));
-      outputFileNameField = XmlHandler.getTagValue(transformNode, 
OUTPUT_FILENAME_FIELD);
-      appendOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(XmlHandler.getTagValue(transformNode, 
APPEND_OUTPUT_FILENAME), ""));
-      outputFileFormat = XmlHandler.getTagValue(transformNode, 
OUTPUT_FILE_FORMAT);
-      outputFileEncoding =
-          Const.NVL(
-              XmlHandler.getTagValue(transformNode, OUTPUT_FILE_ENCODING),
-              Const.getEnvironmentVariable(CONST_FILE_ENCODING, CONST_UTF_8));
-      splitEvery = Const.toInt(XmlHandler.getTagValue(transformNode, 
OUTPUT_SPLIT_EVERY), 0);
-      createParentFolder =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(XmlHandler.getTagValue(transformNode, 
CREATE_PARENT_FOLDER), ""));
-      includeTransformNrInOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(
-                          transformNode, 
INCLUDE_TRANSFORM_NR_IN_OUTPUT_FILENAME),
-                      ""));
-      includePartNrInOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
INCLUDE_PART_NR_IN_OUTPUT_FILENAME),
-                      ""));
-      includeDateInOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
INCLUDE_DATE_IN_OUTPUT_FILENAME), ""));
-      includeTimeInOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
INCLUDE_TIME_IN_OUTPUT_FILENAME), ""));
-      specifyDateFormatOutputFileName =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
SPECIFY_DATE_FORMAT_OUTPUT_FILENAME),
-                      ""));
-      dateFormatOutputFileName = XmlHandler.getTagValue(transformNode, 
DATE_FORMAT_OUTPUT_FILENAME);
-      addOutputFileNameToResult =
-          "Y"
-              .equalsIgnoreCase(
-                  Const.NVL(
-                      XmlHandler.getTagValue(transformNode, 
ADD_OUTPUT_FILENAME_TO_RESULT), ""));
-
-      tokenStartString = XmlHandler.getTagValue(transformNode, 
TOKEN_START_STRING);
-      tokenEndString = XmlHandler.getTagValue(transformNode, TOKEN_END_STRING);
-
-      Node fields = XmlHandler.getSubNode(transformNode, "fields");
-      int nrfields = XmlHandler.countNodes(fields, CONST_FIELD);
-
-      allocate(nrfields);
-
-      for (int i = 0; i < nrfields; i++) {
-        Node fnode = XmlHandler.getSubNodeByNr(fields, CONST_FIELD, i);
-
-        tokenReplacementFields[i] = new TokenReplacementField();
-        tokenReplacementFields[i].setName(XmlHandler.getTagValue(fnode, 
FIELD_NAME));
-        tokenReplacementFields[i].setTokenName(XmlHandler.getTagValue(fnode, 
TOKEN_NAME));
-      }
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, 
"BaseTransformMeta.Exception.ErrorLoadingTransformMeta"), e);
-    }
-  }
-
-  public void allocate(int nrfields) {
-    tokenReplacementFields = new TokenReplacementField[nrfields];
-  }
-
   @Override
   public Object clone() {
-    TokenReplacementMeta retval = (TokenReplacementMeta) super.clone();
-    int nrfields = tokenReplacementFields.length;
-
-    retval.allocate(nrfields);
-
-    for (int i = 0; i < nrfields; i++) {
-      retval.tokenReplacementFields[i] = (TokenReplacementField) 
tokenReplacementFields[i].clone();
-    }
-
-    return retval;
-  }
-
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder(800);
-
-    retval.append("    " + XmlHandler.addTagValue(INPUT_TYPE, inputType));
-    retval.append("    " + XmlHandler.addTagValue(INPUT_TEXT, inputText));
-    retval.append("    " + XmlHandler.addTagValue(INPUT_FIELD_NAME, 
inputFieldName));
-    retval.append("    " + XmlHandler.addTagValue(INPUT_FILENAME, 
inputFileName));
-    retval.append("    " + XmlHandler.addTagValue(INPUT_FILENAME_IN_FIELD, 
inputFileNameInField));
-    retval.append("    " + XmlHandler.addTagValue(INPUT_FILENAME_FIELD, 
inputFileNameField));
-    retval.append(
-        "    " + XmlHandler.addTagValue(ADD_INPUT_FILENAME_TO_RESULT, 
addInputFileNameToResult));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_TYPE, outputType));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FIELD_NAME, 
outputFieldName));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FILENAME, 
outputFileName));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FILENAME_IN_FIELD, 
outputFileNameInField));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FILENAME_FIELD, 
outputFileNameField));
-    retval.append("    " + XmlHandler.addTagValue(APPEND_OUTPUT_FILENAME, 
appendOutputFileName));
-    retval.append("    " + XmlHandler.addTagValue(CREATE_PARENT_FOLDER, 
createParentFolder));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FILE_FORMAT, 
outputFileFormat));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_FILE_ENCODING, 
outputFileEncoding));
-    retval.append("    " + XmlHandler.addTagValue(OUTPUT_SPLIT_EVERY, 
splitEvery));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(
-                INCLUDE_TRANSFORM_NR_IN_OUTPUT_FILENAME, 
includeTransformNrInOutputFileName));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(
-                INCLUDE_PART_NR_IN_OUTPUT_FILENAME, 
includePartNrInOutputFileName));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(INCLUDE_DATE_IN_OUTPUT_FILENAME, 
includeDateInOutputFileName));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(INCLUDE_TIME_IN_OUTPUT_FILENAME, 
includeTimeInOutputFileName));
-    retval.append(
-        "    "
-            + XmlHandler.addTagValue(
-                SPECIFY_DATE_FORMAT_OUTPUT_FILENAME, 
specifyDateFormatOutputFileName));
-    retval.append(
-        "    " + XmlHandler.addTagValue(DATE_FORMAT_OUTPUT_FILENAME, 
dateFormatOutputFileName));
-    retval.append(
-        "    " + XmlHandler.addTagValue(ADD_OUTPUT_FILENAME_TO_RESULT, 
addOutputFileNameToResult));
-
-    retval.append("    " + XmlHandler.addTagValue(TOKEN_START_STRING, 
tokenStartString));
-    retval.append("    " + XmlHandler.addTagValue(TOKEN_END_STRING, 
tokenEndString));
-
-    retval.append("    <fields>").append(Const.CR);
-    for (TokenReplacementField field : tokenReplacementFields) {
-      if (!Utils.isEmpty(field.getName())) {
-        retval.append("      <field>").append(Const.CR);
-        retval.append("        ").append(XmlHandler.addTagValue(FIELD_NAME, 
field.getName()));
-        retval.append("        ").append(XmlHandler.addTagValue(TOKEN_NAME, 
field.getTokenName()));
-        retval.append("      </field>").append(Const.CR);
-      }
-    }
-    retval.append("    </fields>").append(Const.CR);
-
-    return retval.toString();
-  }
-
-  @Override
-  public void setDefault() {
-
-    inputType = "Text";
-    inputFileNameInField = false;
-    addInputFileNameToResult = false;
-
-    outputType = "Field";
-    outputFileNameInField = false;
-    appendOutputFileName = false;
-    createParentFolder = false;
-    includeTransformNrInOutputFileName = false;
-    includePartNrInOutputFileName = false;
-    includeDateInOutputFileName = false;
-    includeTimeInOutputFileName = false;
-    specifyDateFormatOutputFileName = false;
-    addOutputFileNameToResult = false;
-    outputFileEncoding = Const.getEnvironmentVariable(CONST_FILE_ENCODING, 
CONST_UTF_8);
-    outputFileFormat = Const.isWindows() ? "DOS" : "UNIX";
-    splitEvery = 0;
-
-    tokenStartString = "${";
-    tokenEndString = "}";
+    return new TokenReplacementMeta(this);
   }
 
   public String buildFilename(
@@ -628,9 +334,9 @@ public class TokenReplacementMeta
   public String buildFilename(
       String filename,
       IVariables variables,
-      int transformnr,
-      String partnr,
-      int splitnr,
+      int copyNr,
+      String partitionId,
+      int splitNr,
       TokenReplacementMeta meta) {
     SimpleDateFormat daf = new SimpleDateFormat();
 
@@ -665,14 +371,14 @@ public class TokenReplacementMeta
       }
     }
     if (meta.isIncludeTransformNrInOutputFileName()) {
-      retval += "_" + transformnr;
+      retval += "_" + copyNr;
     }
     if (meta.isIncludePartNrInOutputFileName()) {
-      retval += "_" + partnr;
+      retval += "_" + partitionId;
     }
 
     if (meta.getSplitEvery() > 0) {
-      retval += "_" + splitnr;
+      retval += "_" + splitNr;
     }
 
     if (!Utils.isEmpty(extension)) {
@@ -782,9 +488,8 @@ public class TokenReplacementMeta
       IRowMeta[] info,
       TransformMeta nextTransform,
       IVariables variables,
-      IHopMetadataProvider metadataProvider)
-      throws HopTransformException {
-    // change the case insensitive flag too
+      IHopMetadataProvider metadataProvider) {
+    // change the case-insensitive flag too
 
     if (outputType.equalsIgnoreCase(CONST_FIELD)) {
       IValueMeta v = new ValueMetaString(variables.resolve(outputFieldName));
diff --git 
a/plugins/transforms/tokenreplacement/src/test/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMetaTest.java
 
b/plugins/transforms/tokenreplacement/src/test/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMetaTest.java
new file mode 100644
index 0000000000..2415f441d2
--- /dev/null
+++ 
b/plugins/transforms/tokenreplacement/src/test/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementMetaTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.tokenreplacement;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
+import org.junit.jupiter.api.Test;
+
+class TokenReplacementMetaTest {
+  @Test
+  void testSerializationRoundTrip() throws Exception {
+    TokenReplacementMeta meta =
+        TransformSerializationTestUtil.testSerialization(
+            "/token-replacement.xml", TokenReplacementMeta.class);
+
+    assertEquals(3, meta.getTokenReplacementFields().size());
+  }
+}
diff --git 
a/plugins/transforms/tokenreplacement/src/test/resources/token-replacement.xml 
b/plugins/transforms/tokenreplacement/src/test/resources/token-replacement.xml
new file mode 100644
index 0000000000..2d47eefe57
--- /dev/null
+++ 
b/plugins/transforms/tokenreplacement/src/test/resources/token-replacement.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<transform>
+    <input_type>file</input_type>
+    <input_text>text-to-replace</input_text>
+    <input_field_name/>
+    <input_filename>input-filename.txt</input_filename>
+    <input_filename_in_field>Y</input_filename_in_field>
+    <input_filename_field>input-filename-field</input_filename_field>
+    <add_input_filename_to_result>Y</add_input_filename_to_result>
+    <output_type>file</output_type>
+    <output_field_name>output-field-name</output_field_name>
+    <output_filename>output-filename.txt</output_filename>
+    <output_filename_in_field>N</output_filename_in_field>
+    <output_filename_field>output-filename-field</output_filename_field>
+    <append_output_filename>Y</append_output_filename>
+    <create_parent_folder>Y</create_parent_folder>
+    <output_file_format>CR</output_file_format>
+    <output_file_encoding>UTF-8</output_file_encoding>
+    <output_split_every>1234</output_split_every>
+    
<include_transform_nr_in_output_filename>Y</include_transform_nr_in_output_filename>
+    <include_part_nr_in_output_filename>Y</include_part_nr_in_output_filename>
+    <include_date_in_output_filename>Y</include_date_in_output_filename>
+    <include_time_in_output_filename>Y</include_time_in_output_filename>
+    
<specify_date_format_output_filename>Y</specify_date_format_output_filename>
+    
<date_format_output_filename>yyyy-MM-dd&#39;T&#39;HH:mm:ssX</date_format_output_filename>
+    <add_output_filename_to_result>Y</add_output_filename_to_result>
+    <token_start_string>start-string</token_start_string>
+    <token_end_string>end-string</token_end_string>
+    <fields>
+        <field>
+            <field_name>stream-field-1</field_name>
+            <token_name>token-name-1</token_name>
+        </field>
+        <field>
+            <field_name>stream-field-2</field_name>
+            <token_name>token-name-2</token_name>
+        </field>
+        <field>
+            <field_name>stream-field-3</field_name>
+            <token_name>token-name-3</token_name>
+        </field>
+    </fields>
+</transform>

Reply via email to