This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 81bde2d4e7 issue #7288 : add prefix, separator and suffix options to
transform Add Checksum (#7289)
81bde2d4e7 is described below
commit 81bde2d4e79fde12c9872d47b4aeb41020d05b01
Author: Matt Casters <[email protected]>
AuthorDate: Mon Jun 15 12:46:50 2026 +0200
issue #7288 : add prefix, separator and suffix options to transform Add
Checksum (#7289)
---
.../pages/pipeline/transforms/addchecksum.adoc | 6 +
.../transforms/0043-checksum-prefix.hpl | 460 +++++++++++++++++++++
.../transforms/main-0043-checksum.hwf | 72 +++-
.../hop/pipeline/transforms/checksum/CheckSum.java | 94 +++--
.../pipeline/transforms/checksum/CheckSumData.java | 6 +
.../transforms/checksum/CheckSumDialog.java | 66 ++-
.../pipeline/transforms/checksum/CheckSumMeta.java | 69 ++++
.../checksum/messages/messages_en_US.properties | 6 +
.../checksum/CheckSumMetaInjectionTest.java | 3 +
.../transforms/checksum/CheckSumMetaTest.java | 9 +-
.../pipeline/transforms/checksum/CheckSumTest.java | 60 ++-
11 files changed, 780 insertions(+), 71 deletions(-)
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/addchecksum.adoc
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/addchecksum.adoc
index 3b0f5dbd41..4ec4389eea 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/addchecksum.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/addchecksum.adoc
@@ -55,6 +55,12 @@ These are the types that are available:
* SHA-256 : Secure Hash Algorithm 2 : http://en.wikipedia.org/wiki/SHA-256
|Result Type|Some checksum types allow to set different result types: String,
Hexadecimal and Binary
|Result field|The name of the result field containing the checksum
+|Prefix|An optional string to prepend before the field values when building
the input for the checksum calculation.
+This is useful to distinguish hash keys from different topics, for example
`CUST` for customer keys and `PROD` for product keys.
+|Separator|An optional string to insert between the field values when building
the input for the checksum calculation.
+This helps generate more unique hash codes when multiple fields are
concatenated.
+|Suffix|An optional string to append after the field values when building the
input for the checksum calculation.
+Like the prefix, this can be used to distinguish hash keys from different
topics.
|Fields used in the checksum|The names of the fields to include in the
checksum calculation.
Note: You can use the "Get Fields" button to insert all input fields from
previous transforms.
|===
diff --git a/integration-tests/transforms/0043-checksum-prefix.hpl
b/integration-tests/transforms/0043-checksum-prefix.hpl
new file mode 100644
index 0000000000..c8c77127d8
--- /dev/null
+++ b/integration-tests/transforms/0043-checksum-prefix.hpl
@@ -0,0 +1,460 @@
+<?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>
+ <pipeline_version/>
+ <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>
+ <pipeline_type>Normal</pipeline_type>
+ <pipeline_status>0</pipeline_status>
+ <parameters/>
+ <name>0043-checksum</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <created_user>-</created_user>
+ <modified_user>-</modified_user>
+ <created_date>2022/04/29 07:47:12.357</created_date>
+ <modified_date>2022/04/29 07:47:12.357</modified_date>
+ </info>
+ <transform>
+ <type>DataGrid</type>
+ <name>Data grid</name>
+ <fields>
+ <field>
+ <currency/>
+ <decimal/>
+ <group/>
+ <name>product_id</name>
+ <type>Integer</type>
+ <format/>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ </field>
+ <field>
+ <currency/>
+ <decimal/>
+ <group/>
+ <name>customer_id</name>
+ <type>Integer</type>
+ <format/>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ </field>
+ </fields>
+ <data>
+ <line>
+ <item>123</item>
+ <item>123</item>
+ </line>
+ <line>
+ <item>234</item>
+ <item>234</item>
+ </line>
+ <line>
+ <item>345</item>
+ <item>345</item>
+ </line>
+ <line>
+ <item>456</item>
+ <item>456</item>
+ </line>
+ <line>
+ <item>567</item>
+ <item>567</item>
+ </line>
+ </data>
+ <distribute>N</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>96</xloc>
+ <yloc>48</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>customer_hk</name>
+ <fields>
+ <field>
+ <name>customer_id</name>
+ </field>
+ </fields>
+ <resultfieldName>customer_hk</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator/>
+ <prefix>CUST</prefix>
+ <suffix/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>256</xloc>
+ <yloc>48</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>product_hk</name>
+ <fields>
+ <field>
+ <name>product_id</name>
+ </field>
+ </fields>
+ <resultfieldName>product_hk</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator/>
+ <prefix>PROD</prefix>
+ <suffix/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>384</xloc>
+ <yloc>48</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>FilterRows</type>
+ <name>identical hash keys?</name>
+ <compare>
+ <condition>
+ <negated>N</negated>
+ <operator>-</operator>
+ <leftvalue>customer_hk</leftvalue>
+ <function>=</function>
+ <rightvalue>product_hk</rightvalue>
+ <conditions/>
+ </condition>
+ </compare>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>512</xloc>
+ <yloc>48</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>Abort</type>
+ <name>Abort</name>
+ <row_threshold>1</row_threshold>
+ <message>Hash keys with identical values and different prefix need to be
different.</message>
+ <always_log_rows>Y</always_log_rows>
+ <abort_option>ABORT_WITH_ERROR</abort_option>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>640</xloc>
+ <yloc>48</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>without-sep</name>
+ <fields>
+ <field>
+ <name>customer_id</name>
+ </field>
+ <field>
+ <name>product_id</name>
+ </field>
+ </fields>
+ <resultfieldName>hk</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator/>
+ <prefix/>
+ <suffix/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>256</xloc>
+ <yloc>160</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>with-sep</name>
+ <fields>
+ <field>
+ <name>customer_id</name>
+ </field>
+ <field>
+ <name>product_id</name>
+ </field>
+ </fields>
+ <resultfieldName>hk_sep</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator>||</separator>
+ <prefix/>
+ <suffix/>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>384</xloc>
+ <yloc>160</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>FilterRows</type>
+ <name>identical hk?</name>
+ <compare>
+ <condition>
+ <negated>N</negated>
+ <operator>-</operator>
+ <leftvalue>hk</leftvalue>
+ <function>=</function>
+ <rightvalue>hk_sep</rightvalue>
+ <conditions/>
+ </condition>
+ </compare>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>512</xloc>
+ <yloc>160</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>Abort</type>
+ <name>Abort </name>
+ <row_threshold>1</row_threshold>
+ <message>The hash keys with separator should be different from those
without</message>
+ <always_log_rows>Y</always_log_rows>
+ <abort_option>ABORT_WITH_ERROR</abort_option>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>640</xloc>
+ <yloc>160</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>customer_hk </name>
+ <fields>
+ <field>
+ <name>customer_id</name>
+ </field>
+ </fields>
+ <resultfieldName>customer_hk</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator/>
+ <prefix/>
+ <suffix>CUST</suffix>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>256</xloc>
+ <yloc>272</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>CheckSum</type>
+ <name>product_hk </name>
+ <fields>
+ <field>
+ <name>product_id</name>
+ </field>
+ </fields>
+ <resultfieldName>product_hk</resultfieldName>
+ <checksumtype>MD5</checksumtype>
+ <resultType>hexadecimal</resultType>
+ <separator/>
+ <prefix/>
+ <suffix>PROD</suffix>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>384</xloc>
+ <yloc>272</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>FilterRows</type>
+ <name>identical hash keys? </name>
+ <compare>
+ <condition>
+ <negated>N</negated>
+ <operator>-</operator>
+ <leftvalue>customer_hk</leftvalue>
+ <function>=</function>
+ <rightvalue>product_hk</rightvalue>
+ <conditions/>
+ </condition>
+ </compare>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>512</xloc>
+ <yloc>272</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>Abort</type>
+ <name>Abort </name>
+ <row_threshold>1</row_threshold>
+ <message>Hash keys with identical values and different suffix need to be
different.</message>
+ <always_log_rows>Y</always_log_rows>
+ <abort_option>ABORT_WITH_ERROR</abort_option>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>640</xloc>
+ <yloc>272</yloc>
+ </GUI>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <order>
+ <hop>
+ <from>Data grid</from>
+ <to>customer_hk</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>customer_hk</from>
+ <to>product_hk</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>product_hk</from>
+ <to>identical hash keys?</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>identical hash keys?</from>
+ <to>Abort</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>Data grid</from>
+ <to>without-sep</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>without-sep</from>
+ <to>with-sep</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>with-sep</from>
+ <to>identical hk?</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>identical hk?</from>
+ <to>Abort </to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>customer_hk </from>
+ <to>product_hk </to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>product_hk </from>
+ <to>identical hash keys? </to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>identical hash keys? </from>
+ <to>Abort </to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>Data grid</from>
+ <to>customer_hk </to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+ <notepads/>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/transforms/main-0043-checksum.hwf
b/integration-tests/transforms/main-0043-checksum.hwf
index 1c8e056475..ed3c8beca8 100644
--- a/integration-tests/transforms/main-0043-checksum.hwf
+++ b/integration-tests/transforms/main-0043-checksum.hwf
@@ -22,45 +22,71 @@ limitations under the License.
<name_sync_with_filename>Y</name_sync_with_filename>
<description/>
<extended_description/>
- <workflow_version/>
<created_user>-</created_user>
- <created_date>2022/04/29 10:42:16.470</created_date>
<modified_user>-</modified_user>
+ <created_date>2022/04/29 10:42:16.470</created_date>
<modified_date>2022/04/29 10:42:16.470</modified_date>
- <parameters>
- </parameters>
+ <workflow_version/>
+ <parameters/>
<actions>
<action>
- <name>Start</name>
- <description/>
- <type>SPECIAL</type>
- <attributes/>
<repeat>N</repeat>
<schedulerType>0</schedulerType>
<intervalSeconds>0</intervalSeconds>
<intervalMinutes>60</intervalMinutes>
- <hour>12</hour>
- <minutes>0</minutes>
- <weekDay>1</weekDay>
<DayOfMonth>1</DayOfMonth>
- <parallel>N</parallel>
+ <weekDay>1</weekDay>
+ <minutes>0</minutes>
+ <hour>12</hour>
+ <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
<xloc>50</xloc>
<yloc>50</yloc>
+ <parallel>N</parallel>
<attributes_hac/>
</action>
<action>
- <name>Run Checksum Unit Test</name>
- <description/>
- <type>RunPipelineTests</type>
- <attributes/>
<test_names>
<test_name>
<name>0043-checksum UNIT</name>
</test_name>
</test_names>
- <parallel>N</parallel>
+ <name>Run Checksum Unit Test</name>
+ <description/>
+ <type>RunPipelineTests</type>
+ <attributes/>
<xloc>192</xloc>
<yloc>48</yloc>
+ <parallel>N</parallel>
+ <attributes_hac/>
+ </action>
+ <action>
+ <filename>${PROJECT_HOME}/0043-checksum-prefix.hpl</filename>
+ <params_from_previous>N</params_from_previous>
+ <exec_per_row>N</exec_per_row>
+ <clear_rows>N</clear_rows>
+ <clear_files>N</clear_files>
+ <create_parent_folder>N</create_parent_folder>
+ <set_logfile>N</set_logfile>
+ <set_append_logfile>N</set_append_logfile>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <loglevel>Basic</loglevel>
+ <wait_until_finished>Y</wait_until_finished>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <run_configuration>local</run_configuration>
+ <name>0043-checksum-prefix</name>
+ <description/>
+ <type>PIPELINE</type>
+ <attributes/>
+ <xloc>384</xloc>
+ <yloc>48</yloc>
+ <parallel>N</parallel>
<attributes_hac/>
</action>
</actions>
@@ -68,12 +94,18 @@ limitations under the License.
<hop>
<from>Start</from>
<to>Run Checksum Unit Test</to>
- <enabled>Y</enabled>
<evaluation>Y</evaluation>
<unconditional>Y</unconditional>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>Run Checksum Unit Test</from>
+ <to>0043-checksum-prefix</to>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ <enabled>Y</enabled>
</hop>
</hops>
- <notepads>
- </notepads>
+ <notepads/>
<attributes/>
</workflow>
diff --git
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSum.java
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSum.java
index 1dbb8b1e23..0522333aff 100644
---
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSum.java
+++
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSum.java
@@ -22,6 +22,7 @@ import java.security.MessageDigest;
import java.util.zip.Adler32;
import java.util.zip.CRC32;
import org.apache.commons.codec.binary.Hex;
+import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.RowDataUtil;
@@ -84,6 +85,10 @@ public class CheckSum extends BaseTransform<CheckSumMeta,
CheckSumData> {
}
data.fieldnr = data.fieldnrs.length;
+ data.separator = Const.NVL(resolve(meta.getSeparator()), "");
+ data.prefix = Const.NVL(resolve(meta.getPrefix()), "");
+ data.suffix = Const.NVL(resolve(meta.getSuffix()), "");
+
try {
if (meta.getCheckSumType() == CheckSumMeta.CheckSumType.MD5
|| meta.getCheckSumType() == CheckSumMeta.CheckSumType.SHA1
@@ -155,40 +160,67 @@ public class CheckSum extends BaseTransform<CheckSumMeta,
CheckSumData> {
}
private byte[] createCheckSum(Object[] r) throws Exception {
+ byte[] byteArray = buildCheckSumInputBytes(r);
+
+ // Return null when all input values are null.
+ if (byteArray == null) {
+ return null;
+ }
+
+ // Updates the digest using the specified array of bytes
+ data.digest.update(byteArray);
+
+ // Completes the hash computation by performing final operations such as
padding
+ // After digest has been called, the MessageDigest object is reset to its
initialized state
+
+ return data.digest.digest();
+ }
+ private byte[] buildCheckSumInputBytes(Object[] r) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ if (!appendCheckSumContent(baos, r)) {
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ private boolean appendCheckSumContent(ByteArrayOutputStream baos, Object[]
r) throws Exception {
boolean valueAdded = false;
+ boolean fieldValueAdded = false;
+
+ if (!Utils.isEmpty(data.prefix)) {
+ baos.write(data.prefix.getBytes());
+ valueAdded = true;
+ }
// Loop through fields
for (int i = 0; i < data.fieldnr; i++) {
IValueMeta valueMeta = getInputRowMeta().getValueMeta(data.fieldnrs[i]);
+ byte[] fieldBytes = null;
if (valueMeta.isBinary()) {
- byte[] bytes = getInputRowMeta().getBinary(r, data.fieldnrs[i]);
- if (bytes != null) {
- valueAdded = true;
- baos.write(bytes);
- }
+ fieldBytes = getInputRowMeta().getBinary(r, data.fieldnrs[i]);
} else {
Object value = valueMeta.getNativeDataType(r[data.fieldnrs[i]]);
if (value != null) {
- valueAdded = true;
- baos.write(value.toString().getBytes());
+ fieldBytes = value.toString().getBytes();
+ }
+ }
+ if (fieldBytes != null) {
+ if (fieldValueAdded && !Utils.isEmpty(data.separator)) {
+ baos.write(data.separator.getBytes());
}
+ baos.write(fieldBytes);
+ fieldValueAdded = true;
+ valueAdded = true;
}
}
- // Return null when all input values are null.
- if (!valueAdded) {
- return null;
+ if (!Utils.isEmpty(data.suffix)) {
+ baos.write(data.suffix.getBytes());
+ valueAdded = true;
}
- // Updates the digest using the specified array of bytes
- data.digest.update(baos.toByteArray());
-
- // Completes the hash computation by performing final operations such as
padding
- // After digest has been called, the MessageDigest object is reset to its
initialized state
-
- return data.digest.digest();
+ return valueAdded;
}
private static String getStringFromBytes(byte[] bytes) {
@@ -205,37 +237,13 @@ public class CheckSum extends BaseTransform<CheckSumMeta,
CheckSumData> {
private Long calculCheckSum(Object[] r) throws Exception {
Long retval;
- byte[] byteArray;
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- boolean valueAdded = false;
-
- // Loop through fields
- for (int i = 0; i < data.fieldnr; i++) {
- IValueMeta valueMeta = getInputRowMeta().getValueMeta(data.fieldnrs[i]);
-
- if (valueMeta.isBinary()) {
- byte[] bytes = getInputRowMeta().getBinary(r, data.fieldnrs[i]);
- if (bytes != null) {
- valueAdded = true;
- baos.write(bytes);
- }
- } else {
- Object value = valueMeta.getNativeDataType(r[data.fieldnrs[i]]);
- if (value != null) {
- valueAdded = true;
- baos.write(value.toString().getBytes());
- }
- }
- }
+ byte[] byteArray = buildCheckSumInputBytes(r);
// Return null when all input values are null.
- if (!valueAdded) {
+ if (byteArray == null) {
return null;
}
- byteArray = baos.toByteArray();
-
if (meta.getCheckSumType() == CheckSumMeta.CheckSumType.CRC32) {
CRC32 crc32 = new CRC32();
crc32.update(byteArray);
diff --git
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumData.java
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumData.java
index da46ac01ab..9f04031f2a 100644
---
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumData.java
+++
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumData.java
@@ -35,6 +35,12 @@ public class CheckSumData extends BaseTransformData
implements ITransformData {
public MessageDigest digest;
+ public String separator;
+
+ public String prefix;
+
+ public String suffix;
+
public CheckSumData() {
super();
this.nrInfields = 0;
diff --git
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
index cf6fbdc910..7332319fc6 100644
---
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
+++
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
@@ -33,6 +33,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.widget.ColumnInfo;
import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.core.widget.TextVar;
import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
import org.eclipse.swt.SWT;
@@ -58,6 +59,12 @@ public class CheckSumDialog extends BaseTransformDialog {
private Text wResult;
+ private TextVar wPrefix;
+
+ private TextVar wSeparator;
+
+ private TextVar wSuffix;
+
private ColumnInfo[] columnInfo;
private final List<String> inputFields = new ArrayList<>();
@@ -148,13 +155,64 @@ public class CheckSumDialog extends BaseTransformDialog {
fdResult.right = new FormAttachment(100, 0);
wResult.setLayoutData(fdResult);
+ // Prefix
+ Label wlPrefix = new Label(shell, SWT.RIGHT);
+ wlPrefix.setText(BaseMessages.getString(PKG,
"CheckSumDialog.Prefix.Label"));
+ PropsUi.setLook(wlPrefix);
+ FormData fdlPrefix = new FormData();
+ fdlPrefix.left = new FormAttachment(0, 0);
+ fdlPrefix.right = new FormAttachment(middle, -margin);
+ fdlPrefix.top = new FormAttachment(wResult, margin);
+ wlPrefix.setLayoutData(fdlPrefix);
+ wPrefix = new TextVar(variables, shell, SWT.SINGLE | SWT.LEFT |
SWT.BORDER);
+ PropsUi.setLook(wPrefix);
+ FormData fdPrefix = new FormData();
+ fdPrefix.left = new FormAttachment(middle, 0);
+ fdPrefix.top = new FormAttachment(wResult, margin);
+ fdPrefix.right = new FormAttachment(100, 0);
+ wPrefix.setLayoutData(fdPrefix);
+
+ // Separator
+ Label wlSeparator = new Label(shell, SWT.RIGHT);
+ wlSeparator.setText(BaseMessages.getString(PKG,
"CheckSumDialog.Separator.Label"));
+ PropsUi.setLook(wlSeparator);
+ FormData fdlSeparator = new FormData();
+ fdlSeparator.left = new FormAttachment(0, 0);
+ fdlSeparator.right = new FormAttachment(middle, -margin);
+ fdlSeparator.top = new FormAttachment(wPrefix, margin);
+ wlSeparator.setLayoutData(fdlSeparator);
+ wSeparator = new TextVar(variables, shell, SWT.SINGLE | SWT.LEFT |
SWT.BORDER);
+ PropsUi.setLook(wSeparator);
+ FormData fdSeparator = new FormData();
+ fdSeparator.left = new FormAttachment(middle, 0);
+ fdSeparator.top = new FormAttachment(wPrefix, margin);
+ fdSeparator.right = new FormAttachment(100, 0);
+ wSeparator.setLayoutData(fdSeparator);
+
+ // Suffix
+ Label wlSuffix = new Label(shell, SWT.RIGHT);
+ wlSuffix.setText(BaseMessages.getString(PKG,
"CheckSumDialog.Suffix.Label"));
+ PropsUi.setLook(wlSuffix);
+ FormData fdlSuffix = new FormData();
+ fdlSuffix.left = new FormAttachment(0, 0);
+ fdlSuffix.right = new FormAttachment(middle, -margin);
+ fdlSuffix.top = new FormAttachment(wSeparator, margin);
+ wlSuffix.setLayoutData(fdlSuffix);
+ wSuffix = new TextVar(variables, shell, SWT.SINGLE | SWT.LEFT |
SWT.BORDER);
+ PropsUi.setLook(wSuffix);
+ FormData fdSuffix = new FormData();
+ fdSuffix.left = new FormAttachment(middle, 0);
+ fdSuffix.top = new FormAttachment(wSeparator, margin);
+ fdSuffix.right = new FormAttachment(100, 0);
+ wSuffix.setLayoutData(fdSuffix);
+
// Table with fields
Label wlFields = new Label(shell, SWT.NONE);
wlFields.setText(BaseMessages.getString(PKG,
"CheckSumDialog.Fields.Label"));
PropsUi.setLook(wlFields);
FormData fdlFields = new FormData();
fdlFields.left = new FormAttachment(0, 0);
- fdlFields.top = new FormAttachment(wResult, margin);
+ fdlFields.top = new FormAttachment(wSuffix, margin);
wlFields.setLayoutData(fdlFields);
final int nrCols = 1;
@@ -260,6 +318,9 @@ public class CheckSumDialog extends BaseTransformDialog {
wType.setText(input.getCheckSumType().getDescription());
wResult.setText(Const.NVL(input.getResultFieldName(), ""));
wResultType.setText(input.getResultType().getDescription());
+ wPrefix.setText(Const.NVL(input.getPrefix(), ""));
+ wSeparator.setText(Const.NVL(input.getSeparator(), ""));
+ wSuffix.setText(Const.NVL(input.getSuffix(), ""));
Table table = wFields.table;
if (!input.getFields().isEmpty()) {
@@ -290,6 +351,9 @@ public class CheckSumDialog extends BaseTransformDialog {
input.setCheckSumType(CheckSumMeta.CheckSumType.getTypeFromDescription(wType.getText()));
input.setResultFieldName(wResult.getText());
input.setResultType(CheckSumMeta.ResultType.getTypeFromDescription(wResultType.getText()));
+ input.setPrefix(wPrefix.getText());
+ input.setSeparator(wSeparator.getText());
+ input.setSuffix(wSuffix.getText());
input.getFields().clear();
for (TableItem item : wFields.getNonEmptyItems()) {
diff --git
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMeta.java
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMeta.java
index 5b7339c28b..6b74f6e0e7 100644
---
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMeta.java
+++
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMeta.java
@@ -188,6 +188,24 @@ public class CheckSumMeta extends
BaseTransformMeta<CheckSum, CheckSumData> {
injectionKeyDescription = "CheckSum.Injection.RESULT_TYPE")
private ResultType resultType;
+ @HopMetadataProperty(
+ key = "separator",
+ injectionKey = "SEPARATOR",
+ injectionKeyDescription = "CheckSum.Injection.SEPARATOR")
+ private String separator;
+
+ @HopMetadataProperty(
+ key = "prefix",
+ injectionKey = "PREFIX",
+ injectionKeyDescription = "CheckSum.Injection.PREFIX")
+ private String prefix;
+
+ @HopMetadataProperty(
+ key = "suffix",
+ injectionKey = "SUFFIX",
+ injectionKeyDescription = "CheckSum.Injection.SUFFIX")
+ private String suffix;
+
public CheckSumMeta() {
fields = new ArrayList<>();
checkSumType = CheckSumType.CRC32;
@@ -200,6 +218,9 @@ public class CheckSumMeta extends
BaseTransformMeta<CheckSum, CheckSumData> {
meta.checkSumType = checkSumType;
meta.resultFieldName = resultFieldName;
meta.resultType = resultType;
+ meta.separator = separator;
+ meta.prefix = prefix;
+ meta.suffix = suffix;
for (Field field : fields) {
meta.fields.add(new Field(field));
}
@@ -397,4 +418,52 @@ public class CheckSumMeta extends
BaseTransformMeta<CheckSum, CheckSumData> {
public void setResultType(ResultType resultType) {
this.resultType = resultType;
}
+
+ /**
+ * Gets separator
+ *
+ * @return value of separator
+ */
+ public String getSeparator() {
+ return separator;
+ }
+
+ /**
+ * @param separator The separator to set
+ */
+ public void setSeparator(String separator) {
+ this.separator = separator;
+ }
+
+ /**
+ * Gets prefix
+ *
+ * @return value of prefix
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * @param prefix The prefix to set
+ */
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Gets suffix
+ *
+ * @return value of suffix
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ /**
+ * @param suffix The suffix to set
+ */
+ public void setSuffix(String suffix) {
+ this.suffix = suffix;
+ }
}
diff --git
a/plugins/transforms/checksum/src/main/resources/org/apache/hop/pipeline/transforms/checksum/messages/messages_en_US.properties
b/plugins/transforms/checksum/src/main/resources/org/apache/hop/pipeline/transforms/checksum/messages/messages_en_US.properties
index 237303b89f..9754ea8a15 100644
---
a/plugins/transforms/checksum/src/main/resources/org/apache/hop/pipeline/transforms/checksum/messages/messages_en_US.properties
+++
b/plugins/transforms/checksum/src/main/resources/org/apache/hop/pipeline/transforms/checksum/messages/messages_en_US.properties
@@ -20,15 +20,21 @@ CheckSum.Error.Digest=Error setting digest!
CheckSum.Error.ResultFieldMissing=Result fieldname is missing!
CheckSum.ErrorInTransformRunning=Error running transform
CheckSum.Injection.FIELD_NAME=The name of the field.
+CheckSum.Injection.PREFIX=The prefix string to prepend before the field values.
CheckSum.Injection.RESULT_FIELD=The name of the result field containing the
checksum.
CheckSum.Injection.RESULT_TYPE=Checksum result type (String, Hexadecimal and
Binary).
+CheckSum.Injection.SEPARATOR=The separator string to use between field values.
+CheckSum.Injection.SUFFIX=The suffix string to append after the field values.
CheckSum.Injection.TYPE=The type of checksum that needs to be calculated.
CheckSum.Log.CanNotFindField=Can not find field [{0}] in the input stream!
CheckSum.Log.LineNumber=Linenr {0}
CheckSum.Name=Add a checksum
CheckSumDialog.Fieldname.Column=Field
CheckSumDialog.Fields.Label=Fields used in checksum
+CheckSumDialog.Prefix.Label=Prefix
CheckSumDialog.Result.Label=Result field
+CheckSumDialog.Separator.Label=Separator
+CheckSumDialog.Suffix.Label=Suffix
CheckSumDialog.ResultType.Label=Result type
CheckSumDialog.Shell.Title=Add a checksum
CheckSumDialog.TransformName.Label=Transform name
diff --git
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaInjectionTest.java
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaInjectionTest.java
index ba5b9847fe..fead0c954f 100644
---
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaInjectionTest.java
+++
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaInjectionTest.java
@@ -38,6 +38,9 @@ class CheckSumMetaInjectionTest extends
BaseMetadataInjectionTestJunit5<CheckSum
check("RESULT_FIELD", () -> meta.getResultFieldName());
check("TYPE", () -> meta.getCheckSumType(),
CheckSumMeta.CheckSumType.class);
check("RESULT_TYPE", () -> meta.getResultType(),
CheckSumMeta.ResultType.class);
+ check("SEPARATOR", () -> meta.getSeparator());
+ check("PREFIX", () -> meta.getPrefix());
+ check("SUFFIX", () -> meta.getSuffix());
check("FIELD_NAME", () -> meta.getFields().get(0).getName());
}
}
diff --git
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaTest.java
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaTest.java
index 1ea283311d..8669d6424e 100644
---
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaTest.java
+++
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumMetaTest.java
@@ -58,7 +58,14 @@ class CheckSumMetaTest implements IInitializer<CheckSumMeta>
{
@Test
void testSerialization() throws HopException {
List<String> attributes =
- Arrays.asList("fields", "resultFieldName", "checkSumType",
"resultType");
+ Arrays.asList(
+ "fields",
+ "resultFieldName",
+ "checkSumType",
+ "resultType",
+ "separator",
+ "prefix",
+ "suffix");
Map<String, String> getterMap = new HashMap<>();
Map<String, String> setterMap = new HashMap<>();
diff --git
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumTest.java
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumTest.java
index 853f4218ca..436f3ca8c2 100644
---
a/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumTest.java
+++
b/plugins/transforms/checksum/src/test/java/org/apache/hop/pipeline/transforms/checksum/CheckSumTest.java
@@ -87,20 +87,22 @@ class CheckSumTest {
}
}
- private Pipeline buildHexadecimalChecksumPipeline(CheckSumMeta.CheckSumType
checkSumType) {
+ private Pipeline buildHexadecimalChecksumPipeline(
+ CheckSumMeta.CheckSumType checkSumType, CheckSumMeta meta) {
// Create a new pipeline...
PipelineMeta pipelineMeta = new PipelineMeta();
pipelineMeta.setName(getClass().getName());
// Create a CheckSum Transform
String checkSumTransformName = "CheckSum";
- CheckSumMeta meta = new CheckSumMeta();
// Set required fields
meta.setResultFieldName("hex");
meta.setCheckSumType(checkSumType);
meta.setResultType(CheckSumMeta.ResultType.HEXADECIMAL);
- meta.setFields(List.of(new Field("test")));
+ if (meta.getFields().isEmpty()) {
+ meta.setFields(List.of(new Field("test")));
+ }
String checkSumPluginPid =
PluginRegistry.getInstance().getPluginId(TransformPluginType.class,
meta);
@@ -174,7 +176,27 @@ class CheckSumTest {
*/
private MockRowListener executeHexTest(
CheckSumMeta.CheckSumType checkSumType, Object input, IValueMeta meta)
throws Exception {
- Pipeline pipeline = buildHexadecimalChecksumPipeline(checkSumType);
+ CheckSumMeta checkSumMeta = new CheckSumMeta();
+ return executeHexTest(checkSumType, input, meta, checkSumMeta);
+ }
+
+ private MockRowListener executeHexTest(
+ CheckSumMeta.CheckSumType checkSumType,
+ Object input,
+ IValueMeta meta,
+ CheckSumMeta checkSumMeta)
+ throws Exception {
+ return executeHexTest(
+ checkSumType, new Object[] {input}, createStringRowMeta(meta),
checkSumMeta);
+ }
+
+ private MockRowListener executeHexTest(
+ CheckSumMeta.CheckSumType checkSumType,
+ Object[] input,
+ RowMeta inputRowMeta,
+ CheckSumMeta checkSumMeta)
+ throws Exception {
+ Pipeline pipeline = buildHexadecimalChecksumPipeline(checkSumType,
checkSumMeta);
pipeline.prepareExecution();
@@ -183,12 +205,11 @@ class CheckSumTest {
output.addRowListener(listener);
RowProducer rp = pipeline.addRowProducer("CheckSum", 0);
- RowMeta inputRowMeta = createStringRowMeta(meta);
((BaseTransform) pipeline.getRunThread("CheckSum",
0)).setInputRowMeta(inputRowMeta);
pipeline.startThreads();
- rp.putRow(inputRowMeta, new Object[] {input});
+ rp.putRow(inputRowMeta, input);
rp.finished();
pipeline.waitUntilFinished();
@@ -197,6 +218,33 @@ class CheckSumTest {
return listener;
}
+ @Test
+ void testHexOutput_md5WithPrefixSeparatorSuffix() throws Exception {
+ CheckSumMeta checkSumMeta = new CheckSumMeta();
+ checkSumMeta.setPrefix("CUST");
+ checkSumMeta.setSeparator("|");
+ checkSumMeta.setFields(List.of(new Field("field1"), new Field("field2")));
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaString("field1"));
+ inputRowMeta.addValueMeta(new ValueMetaString("field2"));
+
+ MockRowListener results =
+ executeHexTest(
+ CheckSumMeta.CheckSumType.MD5, new Object[] {"123", "456"},
inputRowMeta, checkSumMeta);
+ assertEquals(1, results.getWritten().size());
+ assertEquals("2be9b4ef70bfdf76000b88c22201d74e",
results.getWritten().get(0)[2]);
+
+ checkSumMeta = new CheckSumMeta();
+ checkSumMeta.setPrefix("PROD");
+ checkSumMeta.setFields(List.of(new Field("field1")));
+ results =
+ executeHexTest(
+ CheckSumMeta.CheckSumType.MD5, "123", new
ValueMetaString("field1"), checkSumMeta);
+ assertEquals(1, results.getWritten().size());
+ assertEquals("f096da61d5fd73a19569a76c53998200",
results.getWritten().get(0)[1]);
+ }
+
@Test
void testHexOutput_md5() throws Exception {
MockRowListener results =