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 36af1f6f19 Issue 1932 (XML cleanup Sort Rows) (#6872)
36af1f6f19 is described below

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

    Issue 1932 (XML cleanup Sort Rows) (#6872)
    
    * issue #1932 (XML cleanup Sort Rows, extra unit test, Unit Testing fix for 
not sorting results)
    
    * issue #1932 (test fix and spotless)
---
 integration-tests/transforms/0090-sort-rows.hpl    | 333 +++++++++++++++++
 .../transforms/datasets/data-set-filename.csv      |   8 +-
 .../datasets/golden-sort-rows-asc-insensitive.csv  |   5 +
 .../datasets/golden-sort-rows-asc-sensitive.csv    |   5 +
 .../datasets/golden-sort-rows-desc-sensitive.csv   |   5 +
 .../transforms/main-0090-sort-rows.hwf             |  80 +++++
 .../dataset/golden-sort-rows-asc-insensitive.json  |  24 ++
 .../dataset/golden-sort-rows-asc-sensitive.json    |  24 ++
 .../dataset/golden-sort-rows-desc-insensitive.json |  24 ++
 .../dataset/golden-sort-rows-desc-sensitive.json   |  24 ++
 .../metadata/unit-test/0090-sort-rows UNIT.json    |  81 +++++
 .../org/apache/hop/testing/DataSetCsvUtil.java     |   7 +-
 .../apache/hop/testing/gui/TestingGuiPlugin.java   |  14 +-
 .../org/apache/hop/testing/util/DataSetConst.java  | 400 ++++++++++-----------
 .../hop/pipeline/transforms/sort/SortRows.java     | 170 ++++-----
 .../pipeline/transforms/sort/SortRowsField.java    |  77 ++--
 .../hop/pipeline/transforms/sort/SortRowsMeta.java | 177 ++-------
 17 files changed, 925 insertions(+), 533 deletions(-)

diff --git a/integration-tests/transforms/0090-sort-rows.hpl 
b/integration-tests/transforms/0090-sort-rows.hpl
new file mode 100644
index 0000000000..b40000aaa7
--- /dev/null
+++ b/integration-tests/transforms/0090-sort-rows.hpl
@@ -0,0 +1,333 @@
+<?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>0090-sort-rows</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/25 13:53:26.203</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/03/25 13:53:26.203</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>sample-data</from>
+      <to>name ASC i</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>sample-data</from>
+      <to>name ASC s</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>name ASC i</from>
+      <to>validate ASC i</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>name ASC s</from>
+      <to>validate ASC s</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>name DESC i</from>
+      <to>validate DESC i</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>name DESC s</from>
+      <to>validate DESC s</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>sample-data</from>
+      <to>name DESC i</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>sample-data</from>
+      <to>name DESC s</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>name ASC i</name>
+    <type>SortRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compress>N</compress>
+    <directory>${java.io.tmpdir}</directory>
+    <fields>
+      <field>
+        <ascending>Y</ascending>
+        <case_sensitive>N</case_sensitive>
+        <collator_enabled>N</collator_enabled>
+        <collator_strength>0</collator_strength>
+        <name>name</name>
+        <presorted>N</presorted>
+      </field>
+    </fields>
+    <sort_prefix>out</sort_prefix>
+    <sort_size>1000000</sort_size>
+    <unique_rows>N</unique_rows>
+    <attributes/>
+    <GUI>
+      <xloc>224</xloc>
+      <yloc>32</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>name ASC s</name>
+    <type>SortRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compress>N</compress>
+    <directory>${java.io.tmpdir}</directory>
+    <fields>
+      <field>
+        <ascending>Y</ascending>
+        <case_sensitive>Y</case_sensitive>
+        <collator_enabled>N</collator_enabled>
+        <collator_strength>0</collator_strength>
+        <name>name</name>
+        <presorted>N</presorted>
+      </field>
+    </fields>
+    <sort_prefix>out</sort_prefix>
+    <sort_size>1000000</sort_size>
+    <unique_rows>N</unique_rows>
+    <attributes/>
+    <GUI>
+      <xloc>224</xloc>
+      <yloc>112</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>name DESC i</name>
+    <type>SortRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compress>N</compress>
+    <directory>${java.io.tmpdir}</directory>
+    <fields>
+      <field>
+        <ascending>N</ascending>
+        <case_sensitive>N</case_sensitive>
+        <collator_enabled>N</collator_enabled>
+        <collator_strength>0</collator_strength>
+        <name>name</name>
+        <presorted>N</presorted>
+      </field>
+    </fields>
+    <sort_prefix>out</sort_prefix>
+    <sort_size>1000000</sort_size>
+    <unique_rows>N</unique_rows>
+    <attributes/>
+    <GUI>
+      <xloc>224</xloc>
+      <yloc>192</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>name DESC s</name>
+    <type>SortRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <compress>N</compress>
+    <directory>${java.io.tmpdir}</directory>
+    <fields>
+      <field>
+        <ascending>N</ascending>
+        <case_sensitive>Y</case_sensitive>
+        <collator_enabled>N</collator_enabled>
+        <collator_strength>0</collator_strength>
+        <name>name</name>
+        <presorted>N</presorted>
+      </field>
+    </fields>
+    <sort_prefix>out</sort_prefix>
+    <sort_size>1000000</sort_size>
+    <unique_rows>N</unique_rows>
+    <attributes/>
+    <GUI>
+      <xloc>224</xloc>
+      <yloc>272</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>sample-data</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>N</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>3</item>
+        <item>Hop</item>
+      </line>
+      <line>
+        <item>2</item>
+        <item>Beam</item>
+      </line>
+      <line>
+        <item>1</item>
+        <item>Spark</item>
+      </line>
+      <line>
+        <item>0</item>
+        <item>flink</item>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>3</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>id</name>
+        <format>#</format>
+        <type>Integer</type>
+      </field>
+      <field>
+        <length>100</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>name</name>
+        <type>String</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>96</xloc>
+      <yloc>112</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate ASC i</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>32</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate ASC s</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>112</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate DESC i</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>192</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate DESC s</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>272</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/datasets/data-set-filename.csv 
b/integration-tests/transforms/datasets/data-set-filename.csv
index 3d52d4e002..6859795977 100644
--- a/integration-tests/transforms/datasets/data-set-filename.csv
+++ b/integration-tests/transforms/datasets/data-set-filename.csv
@@ -1,3 +1,5 @@
-field1,field2,field3,target1,target2
-Z,Y,X,One,Two
-W,V,U,Three,Four
+id,name
+1,Spark
+3,Hop
+0,flink
+2,Beam
diff --git 
a/integration-tests/transforms/datasets/golden-sort-rows-asc-insensitive.csv 
b/integration-tests/transforms/datasets/golden-sort-rows-asc-insensitive.csv
new file mode 100644
index 0000000000..939297ae6e
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-sort-rows-asc-insensitive.csv
@@ -0,0 +1,5 @@
+id,name
+2,Beam
+0,flink
+3,Hop
+1,Spark
diff --git 
a/integration-tests/transforms/datasets/golden-sort-rows-asc-sensitive.csv 
b/integration-tests/transforms/datasets/golden-sort-rows-asc-sensitive.csv
new file mode 100644
index 0000000000..86adfe02c8
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-sort-rows-asc-sensitive.csv
@@ -0,0 +1,5 @@
+id,name
+2,Beam
+3,Hop
+1,Spark
+0,flink
diff --git 
a/integration-tests/transforms/datasets/golden-sort-rows-desc-sensitive.csv 
b/integration-tests/transforms/datasets/golden-sort-rows-desc-sensitive.csv
new file mode 100644
index 0000000000..c63a8e1d88
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-sort-rows-desc-sensitive.csv
@@ -0,0 +1,5 @@
+id,name
+0,flink
+1,Spark
+3,Hop
+2,Beam
diff --git a/integration-tests/transforms/main-0090-sort-rows.hwf 
b/integration-tests/transforms/main-0090-sort-rows.hwf
new file mode 100644
index 0000000000..a2318e98e9
--- /dev/null
+++ b/integration-tests/transforms/main-0090-sort-rows.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-0090-sort-rows</name>
+  <name_sync_with_filename>Y</name_sync_with_filename>
+  <description/>
+  <extended_description/>
+  <workflow_version/>
+  <created_user>-</created_user>
+  <created_date>2026/03/25 13:53:08.752</created_date>
+  <modified_user>-</modified_user>
+  <modified_date>2026/03/25 13:53:08.752</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>96</xloc>
+      <yloc>64</yloc>
+      <attributes_hac/>
+    </action>
+    <action>
+      <name>0090-sort-rows UNIT</name>
+      <description/>
+      <type>RunPipelineTests</type>
+      <attributes/>
+      <test_names>
+        <test_name>
+          <name>0090-sort-rows UNIT</name>
+        </test_name>
+      </test_names>
+      <parallel>N</parallel>
+      <xloc>288</xloc>
+      <yloc>64</yloc>
+      <attributes_hac/>
+    </action>
+  </actions>
+  <hops>
+    <hop>
+      <from>Start</from>
+      <to>0090-sort-rows 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-sort-rows-asc-insensitive.json
 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-asc-insensitive.json
new file mode 100644
index 0000000000..1b933126c9
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-asc-insensitive.json
@@ -0,0 +1,24 @@
+{
+  "base_filename": "golden-sort-rows-asc-insensitive.csv",
+  "name": "golden-sort-rows-asc-insensitive",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": 3,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "id",
+      "field_format": "#"
+    },
+    {
+      "field_comment": "",
+      "field_length": 100,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "name",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-sort-rows-asc-sensitive.json
 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-asc-sensitive.json
new file mode 100644
index 0000000000..ff786650ac
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-asc-sensitive.json
@@ -0,0 +1,24 @@
+{
+  "base_filename": "golden-sort-rows-asc-sensitive.csv",
+  "name": "golden-sort-rows-asc-sensitive",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": 3,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "id",
+      "field_format": "#"
+    },
+    {
+      "field_comment": "",
+      "field_length": 100,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "name",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-insensitive.json
 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-insensitive.json
new file mode 100644
index 0000000000..a3dc99105d
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-insensitive.json
@@ -0,0 +1,24 @@
+{
+  "base_filename": "data-set-filename.csv",
+  "name": "golden-sort-rows-desc-insensitive",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": 3,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "id",
+      "field_format": "#"
+    },
+    {
+      "field_comment": "",
+      "field_length": 100,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "name",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-sensitive.json
 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-sensitive.json
new file mode 100644
index 0000000000..b3669dc410
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-sort-rows-desc-sensitive.json
@@ -0,0 +1,24 @@
+{
+  "base_filename": "golden-sort-rows-desc-sensitive.csv",
+  "name": "golden-sort-rows-desc-sensitive",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": 3,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "id",
+      "field_format": "#"
+    },
+    {
+      "field_comment": "",
+      "field_length": 100,
+      "field_type": 2,
+      "field_precision": -1,
+      "field_name": "name",
+      "field_format": ""
+    }
+  ],
+  "folder_name": ""
+}
\ No newline at end of file
diff --git a/integration-tests/transforms/metadata/unit-test/0090-sort-rows 
UNIT.json b/integration-tests/transforms/metadata/unit-test/0090-sort-rows 
UNIT.json
new file mode 100644
index 0000000000..033a85125d
--- /dev/null
+++ b/integration-tests/transforms/metadata/unit-test/0090-sort-rows UNIT.json  
@@ -0,0 +1,81 @@
+{
+  "database_replacements": [],
+  "autoOpening": true,
+  "description": "",
+  "persist_filename": "",
+  "test_type": "UNIT_TEST",
+  "variableValues": [],
+  "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+  "golden_data_sets": [
+    {
+      "field_mappings": [
+        {
+          "transform_field": "id",
+          "data_set_field": "id"
+        },
+        {
+          "transform_field": "name",
+          "data_set_field": "name"
+        }
+      ],
+      "field_order": [
+        ""
+      ],
+      "data_set_name": "golden-sort-rows-asc-insensitive",
+      "transform_name": "validate ASC i"
+    },
+    {
+      "field_mappings": [
+        {
+          "transform_field": "id",
+          "data_set_field": "id"
+        },
+        {
+          "transform_field": "name",
+          "data_set_field": "name"
+        }
+      ],
+      "field_order": [
+        ""
+      ],
+      "data_set_name": "golden-sort-rows-asc-sensitive",
+      "transform_name": "validate ASC s"
+    },
+    {
+      "field_mappings": [
+        {
+          "transform_field": "id",
+          "data_set_field": "id"
+        },
+        {
+          "transform_field": "name",
+          "data_set_field": "name"
+        }
+      ],
+      "field_order": [
+        ""
+      ],
+      "data_set_name": "golden-sort-rows-desc-insensitive",
+      "transform_name": "validate DESC i"
+    },
+    {
+      "field_mappings": [
+        {
+          "transform_field": "id",
+          "data_set_field": "id"
+        },
+        {
+          "transform_field": "name",
+          "data_set_field": "name"
+        }
+      ],
+      "field_order": [],
+      "data_set_name": "golden-sort-rows-desc-sensitive",
+      "transform_name": "validate DESC s"
+    }
+  ],
+  "input_data_sets": [],
+  "name": "0090-sort-rows UNIT",
+  "trans_test_tweaks": [],
+  "pipeline_filename": "./0090-sort-rows.hpl"
+}
\ No newline at end of file
diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
index bf6e56ad66..15e08420d3 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetCsvUtil.java
@@ -25,7 +25,6 @@ import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVParser;
@@ -196,12 +195,10 @@ public class DataSetCsvUtil {
         return new ArrayList<>();
       }
 
-      if (!sortFields.isEmpty()) {
-
+      if (!sortFields.isEmpty() && 
StringUtils.isNotEmpty(sortFields.getFirst())) {
         // Sort the rows...
         //
-        Collections.sort(
-            rows,
+        rows.sort(
             (o1, o2) -> {
               try {
                 return outputRowMeta.compare(o1, o2, sortIndexes);
diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
index e696fe79a0..a87214494b 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
@@ -19,6 +19,7 @@ package org.apache.hop.testing.gui;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang.StringUtils;
@@ -398,7 +399,7 @@ public class TestingGuiPlugin {
       EnterSelectionDialog esd =
           new EnterSelectionDialog(
               hopGui.getShell(),
-              setNames.toArray(new String[setNames.size()]),
+              setNames.toArray(new String[0]),
               BaseMessages.getString(PKG, 
"TestingGuiPlugin.ContextAction.SetGoldenDataset.Header"),
               BaseMessages.getString(
                   PKG, 
"TestingGuiPlugin.ContextAction.SetGoldenDataset.Message"));
@@ -478,6 +479,17 @@ public class TestingGuiPlugin {
       return false;
     }
 
+    // Remove empty values
+    //
+    Iterator<Object[]> orderMappingsIterator = orderMappings.iterator();
+    if (orderMappingsIterator.hasNext()) {
+      Object[] orderFieldLine = orderMappingsIterator.next();
+      String orderField = sortMeta.getString(orderFieldLine, 0);
+      if (StringUtils.isEmpty(orderField)) {
+        orderMappingsIterator.remove();
+      }
+    }
+
     // Modify the test
     //
 
diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
index c4fb6d5089..892e11597b 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
@@ -35,7 +35,6 @@ import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.engine.IPipelineEngine;
 import org.apache.hop.testing.DataSet;
-import org.apache.hop.testing.PipelineTweak;
 import org.apache.hop.testing.PipelineUnitTest;
 import org.apache.hop.testing.PipelineUnitTestFieldMapping;
 import org.apache.hop.testing.PipelineUnitTestSetLocation;
@@ -95,7 +94,7 @@ public class DataSetConst {
    * @return The nr of errors, 0 if no errors found
    * @throws HopException In case there was an error loading data or metadata.
    */
-  public static final int validateTransformResultAgainstUnitTest(
+  public static int validateTransformResultAgainstUnitTest(
       IPipelineEngine<PipelineMeta> pipeline,
       PipelineUnitTest unitTest,
       IHopMetadataProvider metadataProvider,
@@ -198,93 +197,99 @@ public class DataSetConst {
                 comment));
         nrLocationErrors++;
       } else {
-
         // To compare the 2 data sets they need to be explicitly sorted on the 
same keys
         // The added problem is that the user provided a field mapping.
         // So for every "location field order" we need to find the transform 
source field
         //
         // Sort transform result rows
         //
-        final int[] resultFieldIndexes = new 
int[location.getFieldOrder().size()];
-        for (int i = 0; i < resultFieldIndexes.length; i++) {
-          String dataSetOrderField = location.getFieldOrder().get(i);
-          String transformOrderField = 
location.findTransformField(dataSetOrderField);
-          if (transformOrderField == null) {
-            throw new HopException(
-                "There is no transform field provided in the mappings so I 
don't know which field to use to sort '"
-                    + dataSetOrderField
-                    + "'");
+        boolean hasSortOrder =
+            !location.getFieldOrder().isEmpty()
+                && StringUtils.isNotEmpty(location.getFieldOrder().getFirst());
+        if (hasSortOrder) {
+          final int[] resultFieldIndexes = new 
int[location.getFieldOrder().size()];
+          for (int i = 0; i < resultFieldIndexes.length; i++) {
+            String dataSetOrderField = location.getFieldOrder().get(i);
+            String transformOrderField = 
location.findTransformField(dataSetOrderField);
+            if (transformOrderField == null) {
+              throw new HopException(
+                  "There is no transform field provided in the mappings so I 
don't know which field to use to sort '"
+                      + dataSetOrderField
+                      + "'");
+            }
+            resultFieldIndexes[i] = 
resultRowMeta.indexOfValue(transformOrderField);
+            if (resultFieldIndexes[i] < 0) {
+              throw new HopException(
+                  "Unable to find sort field '"
+                      + transformOrderField
+                      + "' in transform results : "
+                      + Arrays.toString(resultRowMeta.getFieldNames()));
+            }
           }
-          resultFieldIndexes[i] = 
resultRowMeta.indexOfValue(transformOrderField);
-          if (resultFieldIndexes[i] < 0) {
+          try {
+            log.logDetailed(
+                "Sorting result rows collection on fields: " + 
location.getFieldOrder());
+            resultCollection
+                .getRows()
+                .sort(
+                    (row1, row2) -> {
+                      try {
+                        return resultRowMeta.compare(row1, row2, 
resultFieldIndexes);
+                      } catch (HopValueException e) {
+                        throw new RuntimeException("Error comparing golden 
data result rows", e);
+                      }
+                    });
+          } catch (RuntimeException e) {
             throw new HopException(
-                "Unable to find sort field '"
-                    + transformOrderField
-                    + "' in transform results : "
-                    + Arrays.toString(resultRowMeta.getFieldNames()));
+                "Error sorting result rows for golden data set '" + 
location.getDataSetName() + "'",
+                e);
           }
-        }
-        try {
-          log.logDetailed("Sorting result rows collection on fields: " + 
location.getFieldOrder());
-          resultCollection
-              .getRows()
-              .sort(
-                  (row1, row2) -> {
-                    try {
-                      return resultRowMeta.compare(row1, row2, 
resultFieldIndexes);
-                    } catch (HopValueException e) {
-                      throw new RuntimeException("Error comparing golden data 
result rows", e);
-                    }
-                  });
-        } catch (RuntimeException e) {
-          throw new HopException(
-              "Error sorting result rows for golden data set '" + 
location.getDataSetName() + "'",
-              e);
-        }
 
-        // Print the first 10 result rows
-        //
-        if (log.isDebug()) {
-          for (int i = 0; i < 10 && i < resultCollection.getRows().size(); 
i++) {
-            log.logDetailed(
-                "Result row #"
-                    + (i + 1)
-                    + " : "
-                    + 
resultRowMeta.getString(resultCollection.getRows().get(i)));
+          // Print the first 10 result rows
+          //
+          if (log.isDebug()) {
+            for (int i = 0; i < 10 && i < resultCollection.getRows().size(); 
i++) {
+              log.logDetailed(
+                  "Result row #"
+                      + (i + 1)
+                      + " : "
+                      + 
resultRowMeta.getString(resultCollection.getRows().get(i)));
+            }
           }
-        }
 
-        // Golden rows
-        //
-        final int[] goldenFieldIndexes = new 
int[location.getFieldOrder().size()];
-        for (int i = 0; i < goldenFieldIndexes.length; i++) {
-          goldenFieldIndexes[i] = 
goldenRowMeta.indexOfValue(location.getFieldOrder().get(i));
-          if (goldenFieldIndexes[i] < 0) {
+          // Golden rows
+          //
+          final int[] goldenFieldIndexes = new 
int[location.getFieldOrder().size()];
+          for (int i = 0; i < goldenFieldIndexes.length; i++) {
+            goldenFieldIndexes[i] = 
goldenRowMeta.indexOfValue(location.getFieldOrder().get(i));
+            if (goldenFieldIndexes[i] < 0) {
+              throw new HopException(
+                  "Unable to find sort field '"
+                      + location.getFieldOrder().get(i)
+                      + "' in golden rows : "
+                      + Arrays.toString(goldenRowMeta.getFieldNames()));
+            }
+          }
+          try {
+            log.logDetailed(
+                "Sorting golden rows collection on fields: " + 
location.getFieldOrder());
+
+            goldenRows.sort(
+                (row1, row2) -> {
+                  try {
+                    return goldenRowMeta.compare(row1, row2, 
goldenFieldIndexes);
+                  } catch (HopValueException e) {
+                    throw new RuntimeException("Error comparing golden data 
set rows", e);
+                  }
+                });
+          } catch (RuntimeException e) {
             throw new HopException(
-                "Unable to find sort field '"
-                    + location.getFieldOrder().get(i)
-                    + "' in golden rows : "
-                    + Arrays.toString(goldenRowMeta.getFieldNames()));
+                "Error sorting golden data rows for golden data set '"
+                    + location.getDataSetName()
+                    + "'",
+                e);
           }
         }
-        try {
-          log.logDetailed("Sorting golden rows collection on fields: " + 
location.getFieldOrder());
-
-          goldenRows.sort(
-              (row1, row2) -> {
-                try {
-                  return goldenRowMeta.compare(row1, row2, goldenFieldIndexes);
-                } catch (HopValueException e) {
-                  throw new RuntimeException("Error comparing golden data set 
rows", e);
-                }
-              });
-        } catch (RuntimeException e) {
-          throw new HopException(
-              "Error sorting golden data rows for golden data set '"
-                  + location.getDataSetName()
-                  + "'",
-              e);
-        }
 
         // Print the first 10 golden rows
         //
@@ -295,124 +300,121 @@ public class DataSetConst {
           }
         }
 
-        if (nrLocationErrors == 0) {
-          final int[] transformFieldIndices = new 
int[location.getFieldMappings().size()];
-          final int[] goldenIndices = new 
int[location.getFieldMappings().size()];
-          for (int i = 0; i < location.getFieldMappings().size(); i++) {
-            PipelineUnitTestFieldMapping fieldMapping = 
location.getFieldMappings().get(i);
+        final int[] transformFieldIndices = new 
int[location.getFieldMappings().size()];
+        final int[] goldenIndices = new 
int[location.getFieldMappings().size()];
+        for (int i = 0; i < location.getFieldMappings().size(); i++) {
+          PipelineUnitTestFieldMapping fieldMapping = 
location.getFieldMappings().get(i);
 
-            transformFieldIndices[i] =
-                
resultRowMeta.indexOfValue(fieldMapping.getTransformFieldName());
-            if (transformFieldIndices[i] < 0) {
-              throw new HopException(
-                  "Unable to find output field '"
-                      + fieldMapping.getTransformFieldName()
-                      + "' while testing output of transform '"
-                      + location.getTransformName()
-                      + "'");
-            }
-            goldenIndices[i] = 
goldenRowMeta.indexOfValue(fieldMapping.getDataSetFieldName());
-            if (goldenIndices[i] < 0) {
-              throw new HopException(
-                  "Unable to find golden data set field '"
-                      + fieldMapping.getDataSetFieldName()
-                      + "' while testing output of transform '"
-                      + location.getTransformName()
-                      + "'");
-            }
-            log.logDetailed(
-                "Field to compare #"
-                    + i
-                    + " found on transform index : "
-                    + transformFieldIndices[i]
-                    + ", golden index : "
-                    + goldenIndices[i]);
+          transformFieldIndices[i] =
+              resultRowMeta.indexOfValue(fieldMapping.getTransformFieldName());
+          if (transformFieldIndices[i] < 0) {
+            throw new HopException(
+                "Unable to find output field '"
+                    + fieldMapping.getTransformFieldName()
+                    + "' while testing output of transform '"
+                    + location.getTransformName()
+                    + "'");
+          }
+          goldenIndices[i] = 
goldenRowMeta.indexOfValue(fieldMapping.getDataSetFieldName());
+          if (goldenIndices[i] < 0) {
+            throw new HopException(
+                "Unable to find golden data set field '"
+                    + fieldMapping.getDataSetFieldName()
+                    + "' while testing output of transform '"
+                    + location.getTransformName()
+                    + "'");
           }
+          log.logDetailed(
+              "Field to compare #"
+                  + i
+                  + " found on transform index : "
+                  + transformFieldIndices[i]
+                  + ", golden index : "
+                  + goldenIndices[i]);
+        }
 
-          for (int rowNumber = 0; rowNumber < resultRows.size(); rowNumber++) {
-            Object[] resultRow = resultRows.get(rowNumber);
-            Object[] goldenRow = goldenRows.get(rowNumber);
+        for (int rowNumber = 0; rowNumber < resultRows.size(); rowNumber++) {
+          Object[] resultRow = resultRows.get(rowNumber);
+          Object[] goldenRow = goldenRows.get(rowNumber);
 
-            // Now compare the input to the golden row
-            //
-            for (int i = 0; i < location.getFieldMappings().size(); i++) {
-              IValueMeta transformValueMeta =
-                  
resultCollection.getRowMeta().getValueMeta(transformFieldIndices[i]);
-              Object transformValue = resultRow[transformFieldIndices[i]];
-
-              IValueMeta goldenValueMeta = 
goldenRowMeta.getValueMeta(goldenIndices[i]);
-              Object goldenValue = goldenRow[goldenIndices[i]];
-
-              if (log.isDetailed()) {
-                log.logDebug(
-                    "Comparing Meta '"
-                        + transformValueMeta.toString()
-                        + "' with '"
-                        + goldenValueMeta.toString()
-                        + "'");
-                log.logDebug("Comparing Value '" + transformValue + "' with '" 
+ goldenValue + "'");
-              }
+          // Now compare the input to the golden row
+          //
+          for (int i = 0; i < location.getFieldMappings().size(); i++) {
+            IValueMeta transformValueMeta =
+                
resultCollection.getRowMeta().getValueMeta(transformFieldIndices[i]);
+            Object transformValue = resultRow[transformFieldIndices[i]];
+
+            IValueMeta goldenValueMeta = 
goldenRowMeta.getValueMeta(goldenIndices[i]);
+            Object goldenValue = goldenRow[goldenIndices[i]];
+
+            if (log.isDetailed()) {
+              log.logDebug(
+                  "Comparing Meta '"
+                      + transformValueMeta.toString()
+                      + "' with '"
+                      + goldenValueMeta.toString()
+                      + "'");
+              log.logDebug("Comparing Value '" + transformValue + "' with '" + 
goldenValue + "'");
+            }
 
-              Object goldenValueConverted;
+            Object goldenValueConverted;
 
-              // sometimes there are data conversion issues because of the the 
database...
-              //
-              if (goldenValueMeta.getType() == transformValueMeta.getType()) {
-                goldenValueConverted = goldenValue;
-              } else {
-                goldenValueConverted = 
transformValueMeta.convertData(goldenValueMeta, goldenValue);
-              }
+            // sometimes there are data conversion issues because of the the 
database...
+            //
+            if (goldenValueMeta.getType() == transformValueMeta.getType()) {
+              goldenValueConverted = goldenValue;
+            } else {
+              goldenValueConverted = 
transformValueMeta.convertData(goldenValueMeta, goldenValue);
+            }
 
-              try {
-                int cmp =
-                    transformValueMeta.compare(
-                        transformValue, goldenValueMeta, goldenValueConverted);
-                if (cmp != 0
-                    && transformValueMeta.isNumber()
-                    && !transformValueMeta.isNull(transformValue)
-                    && !transformValueMeta.isNull(goldenValueConverted)) {
-
-                  // See if it's a floating point issue...
-                  // Convert to an epsilon of 1 millionth.
-                  Double d1 = transformValueMeta.getNumber(transformValue);
-                  Double d2 = 
transformValueMeta.getNumber(goldenValueConverted);
-
-                  if (DoubleMath.fuzzyEquals(d1, d2, 0.000001d)) {
-                    cmp = 0;
-                  }
+            try {
+              int cmp =
+                  transformValueMeta.compare(transformValue, goldenValueMeta, 
goldenValueConverted);
+              if (cmp != 0
+                  && transformValueMeta.isNumber()
+                  && !transformValueMeta.isNull(transformValue)
+                  && !transformValueMeta.isNull(goldenValueConverted)) {
+
+                // See if it's a floating point issue...
+                // Convert to an epsilon of 1 millionth.
+                Double d1 = transformValueMeta.getNumber(transformValue);
+                Double d2 = transformValueMeta.getNumber(goldenValueConverted);
+
+                if (DoubleMath.fuzzyEquals(d1, d2, 0.000001d)) {
+                  cmp = 0;
                 }
-                if (cmp != 0) {
-                  if (log.isDebug()) {
-                    log.logDebug(
-                        "Unit test failure: '" + transformValue + "' <> '" + 
goldenValue + "'");
-                  }
-                  String comment =
-                      "Validation against golden data failed for row number "
-                          + (rowNumber + 1)
-                          + ", field "
-                          + transformValueMeta.getName()
-                          + " : transform value ["
-                          + transformValueMeta.getString(transformValue)
-                          + "] does not correspond to data set value ["
-                          + goldenValueMeta.getString(goldenValue)
-                          + "]";
-                  results.add(
-                      new UnitTestResult(
-                          pipeline.getPipelineMeta().getName(),
-                          unitTest.getName(),
-                          location.getDataSetName(),
-                          location.getTransformName(),
-                          true,
-                          comment));
-                  nrLocationErrors++;
+              }
+              if (cmp != 0) {
+                if (log.isDebug()) {
+                  log.logDebug(
+                      "Unit test failure: '" + transformValue + "' <> '" + 
goldenValue + "'");
                 }
-              } catch (HopValueException e) {
-                throw new HopException(
-                    "Unable to compare transform data against golden data set 
'"
-                        + location.getDataSetName()
-                        + "'",
-                    e);
+                String comment =
+                    "Validation against golden data failed for row number "
+                        + (rowNumber + 1)
+                        + ", field "
+                        + transformValueMeta.getName()
+                        + " : transform value ["
+                        + transformValueMeta.getString(transformValue)
+                        + "] does not correspond to data set value ["
+                        + goldenValueMeta.getString(goldenValue)
+                        + "]";
+                results.add(
+                    new UnitTestResult(
+                        pipeline.getPipelineMeta().getName(),
+                        unitTest.getName(),
+                        location.getDataSetName(),
+                        location.getTransformName(),
+                        true,
+                        comment));
+                nrLocationErrors++;
               }
+            } catch (HopValueException e) {
+              throw new HopException(
+                  "Unable to compare transform data against golden data set '"
+                      + location.getDataSetName()
+                      + "'",
+                  e);
             }
           }
         }
@@ -447,7 +449,7 @@ public class DataSetConst {
     return nrErrors;
   }
 
-  public static final String getDirectoryFromPath(String path) {
+  public static String getDirectoryFromPath(String path) {
     int lastSlashIndex = path.lastIndexOf('/');
     if (lastSlashIndex >= 0) {
       return path.substring(0, lastSlashIndex);
@@ -456,15 +458,6 @@ public class DataSetConst {
     }
   }
 
-  public static final String getNameFromPath(String path) {
-    int lastSlashIndex = path.lastIndexOf('/');
-    if (lastSlashIndex >= 0) {
-      return path.substring(lastSlashIndex + 1);
-    } else {
-      return path;
-    }
-  }
-
   public static IRowMeta getTransformOutputFields(
       DataSet dataSet, PipelineUnitTestSetLocation inputLocation) throws 
HopException {
     IRowMeta dataSetRowMeta = dataSet.getSetRowMeta();
@@ -491,24 +484,7 @@ public class DataSetConst {
     return outputRowMeta;
   }
 
-  /**
-   * Get the PipelineTweak for a tweak description (from the dialog)
-   *
-   * @param tweakDescription The description to look for
-   * @return the tweak or NONE if nothing matched
-   */
-  public PipelineTweak getTweakForDescription(String tweakDescription) {
-    if (StringUtils.isEmpty(tweakDescription)) {
-      return PipelineTweak.NONE;
-    }
-    int index = Const.indexOfString(tweakDescription, tweakDesc);
-    if (index < 0) {
-      return PipelineTweak.NONE;
-    }
-    return PipelineTweak.values()[index];
-  }
-
-  public static final String getTestTypeDescription(TestType testType) {
+  public static String getTestTypeDescription(TestType testType) {
     int index = 0; // DEVELOPMENT
     if (testType != null) {
       TestType[] testTypes = TestType.values();
@@ -529,7 +505,7 @@ public class DataSetConst {
    * @param testTypeDescription The description to look for
    * @return the test type or NONE if nothing matched
    */
-  public static final TestType getTestTypeForDescription(String 
testTypeDescription) {
+  public static TestType getTestTypeForDescription(String testTypeDescription) 
{
     if (StringUtils.isEmpty(testTypeDescription)) {
       return TestType.DEVELOPMENT;
     }
@@ -540,7 +516,7 @@ public class DataSetConst {
     return TestType.values()[index];
   }
 
-  public static final String[] getTestTypeDescriptions() {
+  public static String[] getTestTypeDescriptions() {
     return testTypeDesc;
   }
 }
diff --git 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
index 45db5ea7b5..2cfe9bb03a 100644
--- 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
+++ 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRows.java
@@ -255,12 +255,9 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
           if (data.compressFiles) {
             di =
                 getDataInputStream(
-                    new GZIPInputStream(
-                        new BufferedInputStream(data.fis.get(data.fis.size() - 
1))));
+                    new GZIPInputStream(new 
BufferedInputStream(data.fis.getLast())));
           } else {
-            di =
-                new DataInputStream(
-                    new BufferedInputStream(data.fis.get(data.fis.size() - 1), 
50000));
+            di = new DataInputStream(new 
BufferedInputStream(data.fis.getLast(), 50000));
           }
           data.dis.add(di);
 
@@ -281,7 +278,7 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
         }
 
         // Sort the data row buffer
-        Collections.sort(data.tempRows, data.comparator);
+        data.tempRows.sort(data.comparator);
       } catch (Exception e) {
         logError(BaseMessages.getString(PKG, 
"SortRows.Error.ErrorReadingBackTempFiles"), e);
       }
@@ -313,7 +310,7 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
           }
         }
 
-        RowTempFile rowTempFile = data.tempRows.remove(0);
+        RowTempFile rowTempFile = data.tempRows.removeFirst();
         retval = rowTempFile.row;
         int smallest = rowTempFile.fileNumber;
 
@@ -385,83 +382,15 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
 
   @Override
   public boolean processRow() throws HopException {
-
-    // wait for first for is available
     Object[] r = getRow();
 
-    List<SortRowsField> groupFields = null;
-
     if (first) {
       this.first = false;
 
-      // do we have any row at start processing?
-      if (r == null) {
-        // seems that we don't
-        this.setOutputDone();
+      if (processRowFirstCall(r)) {
         return false;
       }
-
-      IRowMeta inputRowMeta = getInputRowMeta();
-
-      // do we have group numbers?
-      if (meta.isGroupSortEnabled()) {
-        data.newBatch = true;
-
-        // we do set exact list instead of null
-        groupFields = meta.getGroupFields();
-        data.groupnrs = new int[groupFields.size()];
-
-        for (int i = 0; i < data.groupnrs.length; i++) {}
-
-        for (int i = 0; i < groupFields.size(); i++) {
-          data.groupnrs[i] = 
inputRowMeta.indexOfValue(groupFields.get(i).getFieldName());
-          if (data.groupnrs[i] < 0) {
-            logError(
-                BaseMessages.getString(
-                    PKG, "SortRows.Error.PresortedFieldNotFound", 
groupFields.get(i)));
-            setErrors(1);
-            stopAll();
-            return false;
-          }
-        }
-      }
-
-      String[] fieldNames = new String[meta.getSortFields().size()];
-      for (int i = 0; i < fieldNames.length; i++) {
-        fieldNames[i] = meta.getSortFields().get(i).getFieldName();
-      }
-      data.fieldnrs = new int[fieldNames.length];
-      List<Integer> toConvert = new ArrayList<>();
-
-      // Metadata
-      data.outputRowMeta = inputRowMeta.clone();
-      meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
-      data.comparator = new RowTemapFileComparator(data.outputRowMeta, 
data.fieldnrs);
-
-      for (int i = 0; i < meta.getSortFields().size(); i++) {
-        data.fieldnrs[i] = 
inputRowMeta.indexOfValue(meta.getSortFields().get(i).getFieldName());
-        if (data.fieldnrs[i] < 0) {
-          throw new HopException(
-              BaseMessages.getString(
-                  PKG,
-                  "SortRowsMeta.CheckResult.TransformFieldNotInInputStream",
-                  meta.getSortFields().get(i).getFieldName(),
-                  getTransformName()));
-        }
-        // do we need binary conversion for this type?
-        if 
(inputRowMeta.getValueMeta(data.fieldnrs[i]).isStorageBinaryString()) {
-          toConvert.add(data.fieldnrs[i]);
-        }
-      }
-
-      data.convertKeysToNative = toConvert.isEmpty() ? null : new 
int[toConvert.size()];
-      int i = 0;
-      for (Integer in : toConvert) {
-        data.convertKeysToNative[i] = in;
-        i++;
-      }
-      data.rowComparator = new RowObjectArrayComparator(data.outputRowMeta, 
data.fieldnrs);
-    } // end if first
+    }
 
     // it is not first row and it is null
     if (r == null) {
@@ -472,34 +401,26 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
       return false;
     }
 
-    // if Group Sort is not enabled then do the normal sort.
-    if (!meta.isGroupSortEnabled()) {
+    if (data.newBatch) {
+      data.newBatch = false;
+      setPrevious(r);
+      // this enables Sort stuff to initialize its state.
       this.addBuffer(getInputRowMeta(), r);
     } else {
-      // Otherwise do grouping sort
-      if (data.newBatch) {
-        data.newBatch = false;
-        setPrevious(r);
-        // this enables Sort stuff to initialize it's state.
+      if (this.sameGroup(data.previous, r)) {
+        // this performs normal row collection functionality.
         this.addBuffer(getInputRowMeta(), r);
       } else {
-        if (this.sameGroup(data.previous, r)) {
-          // setPrevious( r ); // we are not need to set it every time
+        this.preSortBeforeFlush();
 
-          // this performs SortRows normal row collection functionality.
-          this.addBuffer(getInputRowMeta(), r);
-        } else {
-          this.preSortBeforeFlush();
-
-          // flush sorted block to next transform:
-          this.passBuffer();
+        // flush sorted block to next transform:
+        this.passBuffer();
 
-          // new sorted block beginning
-          setPrevious(r);
-          data.newBatch = true;
+        // new sorted block beginning
+        setPrevious(r);
+        data.newBatch = true;
 
-          this.addBuffer(getInputRowMeta(), r);
-        }
+        this.addBuffer(getInputRowMeta(), r);
       }
     }
 
@@ -510,6 +431,50 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
     return true;
   }
 
+  private boolean processRowFirstCall(Object[] r) throws HopException {
+    // do we have any row at start processing?
+    if (r == null) {
+      // seems that we don't
+      this.setOutputDone();
+      return true;
+    }
+
+    IRowMeta inputRowMeta = getInputRowMeta();
+
+    data.fieldnrs = new int[meta.getSortFields().size()];
+    List<Integer> toConvert = new ArrayList<>();
+
+    // Metadata
+    data.outputRowMeta = inputRowMeta.clone();
+    meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, 
metadataProvider);
+    data.comparator = new RowTemapFileComparator(data.outputRowMeta, 
data.fieldnrs);
+
+    for (int i = 0; i < meta.getSortFields().size(); i++) {
+      data.fieldnrs[i] = 
inputRowMeta.indexOfValue(meta.getSortFields().get(i).getFieldName());
+      if (data.fieldnrs[i] < 0) {
+        throw new HopException(
+            BaseMessages.getString(
+                PKG,
+                "SortRowsMeta.CheckResult.TransformFieldNotInInputStream",
+                meta.getSortFields().get(i).getFieldName(),
+                getTransformName()));
+      }
+      // do we need binary conversion for this type?
+      if (inputRowMeta.getValueMeta(data.fieldnrs[i]).isStorageBinaryString()) 
{
+        toConvert.add(data.fieldnrs[i]);
+      }
+    }
+
+    data.convertKeysToNative = toConvert.isEmpty() ? null : new 
int[toConvert.size()];
+    int i = 0;
+    for (Integer in : toConvert) {
+      data.convertKeysToNative[i] = in;
+      i++;
+    }
+    data.rowComparator = new RowObjectArrayComparator(data.outputRowMeta, 
data.fieldnrs);
+    return false;
+  }
+
   /**
    * This method passes all rows in the buffer to the next transforms. Usually 
call to this method
    * indicates that this particular transform finishing processing.
@@ -639,7 +604,7 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
   /** Sort the entire vector, if it is not empty. */
   void quickSort(List<Object[]> elements) {
     if (CollectionUtils.isNotEmpty(elements)) {
-      Collections.sort(elements, data.rowComparator);
+      elements.sort(data.rowComparator);
 
       long nrConversions = 0L;
       for (IValueMeta valueMeta : data.outputRowMeta.getValueMetaList()) {
@@ -654,11 +619,6 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
     }
   }
 
-  @Override
-  public void startBundle() throws HopException {
-    // Do nothing
-  }
-
   /**
    * Calling this method will alert the transform that we finished passing 
records to the transform.
    * Specifically for transforms like "Sort Rows" it means that the buffered 
rows can be sorted and
@@ -708,7 +668,7 @@ public class SortRows extends BaseTransform<SortRowsMeta, 
SortRowsData> {
     }
   }
 
-  private class SortRowsComparator {
+  private static class SortRowsComparator {
     protected IRowMeta rowMeta;
     protected int[] fieldNrs;
 
diff --git 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsField.java
 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsField.java
index 673517435a..be7291228c 100644
--- 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsField.java
+++ 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsField.java
@@ -17,27 +17,13 @@
 
 package org.apache.hop.pipeline.transforms.sort;
 
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 
+@Getter
+@Setter
 public class SortRowsField {
-
-  public SortRowsField() {}
-
-  public SortRowsField(
-      String fieldName,
-      boolean ascending,
-      boolean caseSensitive,
-      boolean collatorEnabled,
-      int collatorStrength,
-      boolean preSortedField) {
-    this.fieldName = fieldName;
-    this.ascending = ascending;
-    this.caseSensitive = caseSensitive;
-    this.collatorEnabled = collatorEnabled;
-    this.collatorStrength = collatorStrength;
-    this.preSortedField = preSortedField;
-  }
-
   /** order by which fields? */
   @HopMetadataProperty(key = "name", injectionKey = "NAME")
   private String fieldName;
@@ -62,51 +48,30 @@ public class SortRowsField {
   @HopMetadataProperty(key = "presorted", injectionKey = "PRESORTED")
   private boolean preSortedField = false;
 
-  public String getFieldName() {
-    return fieldName;
-  }
+  public SortRowsField() {}
 
-  public void setFieldName(String fieldName) {
+  public SortRowsField(
+      String fieldName,
+      boolean ascending,
+      boolean caseSensitive,
+      boolean collatorEnabled,
+      int collatorStrength,
+      boolean preSortedField) {
     this.fieldName = fieldName;
-  }
-
-  public boolean isAscending() {
-    return ascending;
-  }
-
-  public void setAscending(boolean ascending) {
     this.ascending = ascending;
-  }
-
-  public boolean isCaseSensitive() {
-    return caseSensitive;
-  }
-
-  public void setCaseSensitive(boolean caseSensitive) {
     this.caseSensitive = caseSensitive;
-  }
-
-  public boolean isCollatorEnabled() {
-    return collatorEnabled;
-  }
-
-  public void setCollatorEnabled(boolean collatorEnabled) {
     this.collatorEnabled = collatorEnabled;
-  }
-
-  public int getCollatorStrength() {
-    return collatorStrength;
-  }
-
-  public void setCollatorStrength(int collatorStrength) {
     this.collatorStrength = collatorStrength;
+    this.preSortedField = preSortedField;
   }
 
-  public boolean isPreSortedField() {
-    return preSortedField;
-  }
-
-  public void setPreSortedField(boolean preSortedField) {
-    this.preSortedField = preSortedField;
+  public SortRowsField(SortRowsField f) {
+    this();
+    this.fieldName = f.fieldName;
+    this.ascending = f.ascending;
+    this.caseSensitive = f.caseSensitive;
+    this.collatorEnabled = f.collatorEnabled;
+    this.collatorStrength = f.collatorStrength;
+    this.preSortedField = f.preSortedField;
   }
 }
diff --git 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsMeta.java
 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsMeta.java
index 3d64e1073a..02507087dd 100644
--- 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsMeta.java
+++ 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsMeta.java
@@ -24,6 +24,8 @@ import java.text.Collator;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.ICheckResult;
@@ -47,6 +49,8 @@ import org.apache.hop.pipeline.transform.TransformMeta;
     categoryDescription = 
"i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Transform",
     keywords = "i18n::SortRowsMeta.keyword",
     documentationUrl = "/pipeline/transforms/sort.html")
+@Getter
+@Setter
 public class SortRowsMeta extends BaseTransformMeta<SortRows, SortRowsData>
     implements Serializable {
   @Serial private static final long serialVersionUID = -9075883720765645655L;
@@ -56,7 +60,7 @@ public class SortRowsMeta extends BaseTransformMeta<SortRows, 
SortRowsData>
   private static final String CONST_FIELD = "field";
 
   @HopMetadataProperty(groupKey = "fields", key = "field", injectionGroupKey = 
"FIELDS")
-  private List<SortRowsField> sortFields = new ArrayList<>();
+  private List<SortRowsField> sortFields;
 
   /** Directory to store the temp files */
   @HopMetadataProperty(key = "directory", injectionKey = "SORT_DIRECTORY")
@@ -92,57 +96,9 @@ public class SortRowsMeta extends 
BaseTransformMeta<SortRows, SortRowsData>
   @HopMetadataProperty(key = "compress_variables", injectionKey = 
"COMPRESS_VARIABLE")
   private String compressFilesVariable;
 
-  private List<SortRowsField> groupFields;
-
   public SortRowsMeta() {
-    super(); // allocate BaseTransformMeta
-  }
-
-  /**
-   * @return Returns the directory.
-   */
-  public String getDirectory() {
-    return directory;
-  }
-
-  /**
-   * @param directory The directory to set.
-   */
-  public void setDirectory(String directory) {
-    this.directory = directory;
-  }
-
-  /**
-   * @return Returns the prefix.
-   */
-  public String getPrefix() {
-    return prefix;
-  }
-
-  /**
-   * @param prefix The prefix to set.
-   */
-  public void setPrefix(String prefix) {
-    this.prefix = prefix;
-  }
-
-  public List<SortRowsField> getSortFields() {
-    return sortFields;
-  }
-
-  public void setSortFields(List<SortRowsField> sortFields) {
-    this.sortFields = sortFields;
-  }
-
-  @Override
-  public Object clone() {
-    SortRowsMeta retval = (SortRowsMeta) super.clone();
-
-    return retval;
-  }
-
-  @Override
-  public void setDefault() {
+    super();
+    sortFields = new ArrayList<>();
     directory = "${java.io.tmpdir}";
     prefix = "out";
     sortSize = "1000000";
@@ -150,18 +106,29 @@ public class SortRowsMeta extends 
BaseTransformMeta<SortRows, SortRowsData>
     compressFiles = false;
     compressFilesVariable = null;
     onlyPassingUniqueRows = false;
+  }
 
-    int nrFields = 0;
+  public SortRowsMeta(SortRowsMeta m) {
+    this();
+    this.directory = m.directory;
+    this.prefix = m.prefix;
+    this.sortSize = m.sortSize;
+    this.freeMemoryLimit = m.freeMemoryLimit;
+    this.onlyPassingUniqueRows = m.onlyPassingUniqueRows;
+    this.compressFiles = m.compressFiles;
+    this.compressFilesVariable = m.compressFilesVariable;
+    m.sortFields.forEach(f -> sortFields.add(new SortRowsField(f)));
   }
 
-  // Returns the default collation strength based on the users' default locale.
-  // Package protected for testing purposes
-  int getDefaultCollationStrength() {
-    return getDefaultCollationStrength(Locale.getDefault());
+  @Override
+  public Object clone() {
+    return new SortRowsMeta(this);
   }
 
-  // Returns the collation strength based on the passed in locale.
-  // Package protected for testing purposes
+  /**
+   * Returns the collation strength based on the passed in locale. Package 
protected for testing
+   * purposes
+   */
   int getDefaultCollationStrength(Locale aLocale) {
     int defaultStrength = Collator.IDENTICAL;
     if (aLocale != null) {
@@ -195,6 +162,7 @@ public class SortRowsMeta extends 
BaseTransformMeta<SortRows, SortRowsData>
         valueMeta.setCaseInsensitive(!field.isCaseSensitive());
         valueMeta.setCollatorDisabled(!field.isCollatorEnabled());
         valueMeta.setCollatorStrength(field.getCollatorStrength());
+
         // Also see if lazy conversion is active on these key fields.
         // If so we want to automatically convert them to the normal storage 
type.
         // This will improve performance
@@ -320,97 +288,4 @@ public class SortRowsMeta extends 
BaseTransformMeta<SortRows, SortRowsData>
       remarks.add(cr);
     }
   }
-
-  /**
-   * @return Returns the sortSize.
-   */
-  public String getSortSize() {
-    return sortSize;
-  }
-
-  /**
-   * @param sortSize The sortSize to set.
-   */
-  public void setSortSize(String sortSize) {
-    this.sortSize = sortSize;
-  }
-
-  /**
-   * @return Returns whether temporary files should be compressed
-   */
-  public boolean isCompressFiles() {
-    return compressFiles;
-  }
-
-  /**
-   * @param compressFiles Whether to compress temporary files created during 
sorting
-   */
-  public void setCompressFiles(boolean compressFiles) {
-    this.compressFiles = compressFiles;
-  }
-
-  /**
-   * @return the onlyPassingUniqueRows
-   */
-  public boolean isOnlyPassingUniqueRows() {
-    return onlyPassingUniqueRows;
-  }
-
-  /**
-   * @param onlyPassingUniqueRows the onlyPassingUniqueRows to set
-   */
-  public void setOnlyPassingUniqueRows(boolean onlyPassingUniqueRows) {
-    this.onlyPassingUniqueRows = onlyPassingUniqueRows;
-  }
-
-  /**
-   * @return the compressFilesVariable
-   */
-  public String getCompressFilesVariable() {
-    return compressFilesVariable;
-  }
-
-  /**
-   * @param compressFilesVariable the compressFilesVariable to set
-   */
-  public void setCompressFilesVariable(String compressFilesVariable) {
-    this.compressFilesVariable = compressFilesVariable;
-  }
-
-  /**
-   * @return the freeMemoryLimit
-   */
-  public String getFreeMemoryLimit() {
-    return freeMemoryLimit;
-  }
-
-  /**
-   * @param freeMemoryLimit the freeMemoryLimit to set
-   */
-  public void setFreeMemoryLimit(String freeMemoryLimit) {
-    this.freeMemoryLimit = freeMemoryLimit;
-  }
-
-  public boolean isGroupSortEnabled() {
-    return this.getSortFields() != null;
-  }
-
-  public void setGroupFields(List<SortRowsField> groupFields) {
-    this.groupFields = groupFields;
-  }
-
-  public List<SortRowsField> getGroupFields() {
-    if (this.groupFields == null) {
-      groupFields = new ArrayList<>();
-      for (int i = 0; i < getSortFields().size(); i++) {
-        if (getSortFields().get(i).isPreSortedField()) {
-          //          if (groupFields == null) {
-          //            groupFields = new ArrayList<>();
-          //          }
-          groupFields.add(getSortFields().get(i));
-        }
-      }
-    }
-    return groupFields;
-  }
 }

Reply via email to