This is an automated email from the ASF dual-hosted git repository.
bamaer 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 1535ba3254 data format mask is not inherited correctly, fixes #8956
(#8959)
1535ba3254 is described below
commit 1535ba3254b2e86bec4f85ed8bde01ea1423ad02
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Fri Oct 16 14:13:51 2026 +0200
data format mask is not inherited correctly, fixes #8956 (#8959)
---
...ext-file-output-inherited-formats-read-back.hpl | 262 +++++++++++++
.../0123-text-file-output-inherited-formats.hpl | 418 +++++++++++++++++++++
.../datasets/golden-inherited-formats.csv | 3 +
.../transforms/files/inherited-formats.csv | 3 +
...ain-0123-text-file-output-inherited-formats.hwf | 115 ++++++
.../metadata/dataset/0108-data-set-output.json | 1 +
.../metadata/dataset/golden-inherited-formats.json | 63 ++++
...le-output-inherited-formats-read-back UNIT.json | 52 +++
.../textfileoutput/TextFileOutputMeta.java | 2 +-
.../TextFileOutputFormatInheritanceTest.java | 110 ++++++
10 files changed, 1028 insertions(+), 1 deletion(-)
diff --git
a/integration-tests/transforms/0123-text-file-output-inherited-formats-read-back.hpl
b/integration-tests/transforms/0123-text-file-output-inherited-formats-read-back.hpl
new file mode 100644
index 0000000000..2c6dc09097
--- /dev/null
+++
b/integration-tests/transforms/0123-text-file-output-inherited-formats-read-back.hpl
@@ -0,0 +1,262 @@
+<?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>0123-text-file-output-inherited-formats-read-back</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>Reads the file written by
0123-text-file-output-inherited-formats as plain text so the unit test can
compare every written value with the expected formatting.</description>
+ <extended_description/>
+ <created_user>-</created_user>
+ <modified_user>-</modified_user>
+ <created_date>2026/10/16 12:00:00.000</created_date>
+ <modified_date>2026/10/16 12:00:00.000</modified_date>
+ </info>
+ <transform>
+ <type>TextFileInput2</type>
+ <name>Read written text</name>
+ <file_type>CSV</file_type>
+ <compression>None</compression>
+ <separator>;</separator>
+ <enclosure>"</enclosure>
+ <enclosure_breaks>N</enclosure_breaks>
+ <escapechar/>
+ <header>Y</header>
+ <prependFileName>N</prependFileName>
+ <nr_headerlines>1</nr_headerlines>
+ <footer>N</footer>
+ <nr_footerlines>1</nr_footerlines>
+ <line_wrapped>N</line_wrapped>
+ <nr_wraps>1</nr_wraps>
+ <layout_paged>N</layout_paged>
+ <nr_lines_per_page>80</nr_lines_per_page>
+ <nr_lines_doc_header>0</nr_lines_doc_header>
+ <noempty>Y</noempty>
+ <include>N</include>
+ <include_field/>
+ <rownum>N</rownum>
+ <rownum_field/>
+ <rownumByFile>N</rownumByFile>
+ <format>mixed</format>
+ <encoding>UTF-8</encoding>
+ <limit>0</limit>
+ <date_format_lenient>N</date_format_lenient>
+ <date_format_locale>en_US</date_format_locale>
+ <length>Characters</length>
+ <filters/>
+ <error_count_field/>
+ <error_fields_field/>
+ <error_text_field/>
+ <error_line_skipped>N</error_line_skipped>
+ <schema_definition/>
+ <ignore_fields>N</ignore_fields>
+ <file>
+ <name>${java.io.tmpdir}/0123/inherited-formats-out.csv</name>
+ <filemask/>
+ <exclude_filemask/>
+ <file_required>Y</file_required>
+ <include_subfolders>N</include_subfolders>
+ <type>CSV</type>
+ <compression>None</compression>
+ </file>
+ <files>
+ <file>
+ <name>${java.io.tmpdir}/0123/inherited-formats-out.csv</name>
+ <filemask/>
+ <exclude_filemask/>
+ <file_required>Y</file_required>
+ <include_subfolders>N</include_subfolders>
+ </file>
+ </files>
+ <accept_filenames>N</accept_filenames>
+ <accept_transform_name/>
+ <passing_through_fields>N</passing_through_fields>
+ <accept_field/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <error_ignored>N</error_ignored>
+ <file_error_field/>
+ <file_error_message_field/>
+ <skip_bad_files>N</skip_bad_files>
+ <bad_line_files_destination_directory/>
+ <bad_line_files_extension>warning</bad_line_files_extension>
+ <error_line_files_destination_directory/>
+ <error_line_files_extension>error</error_line_files_extension>
+ <line_number_files_destination_directory/>
+ <line_number_files_extension>line</line_number_files_extension>
+ <fields>
+ <field>
+ <name>id</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_input</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_select</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_override</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_default</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>n_input</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>s_value</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ </fields>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>128</xloc>
+ <yloc>96</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>Dummy</type>
+ <name>OUTPUT</name>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>352</xloc>
+ <yloc>96</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <order>
+ <hop>
+ <from>Read written text</from>
+ <to>OUTPUT</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+ <notepads/>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git
a/integration-tests/transforms/0123-text-file-output-inherited-formats.hpl
b/integration-tests/transforms/0123-text-file-output-inherited-formats.hpl
new file mode 100644
index 0000000000..ac53063eb2
--- /dev/null
+++ b/integration-tests/transforms/0123-text-file-output-inherited-formats.hpl
@@ -0,0 +1,418 @@
+<?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>0123-text-file-output-inherited-formats</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>Text File Output fields with a blank Format inherit the
conversion mask of the incoming field. d_input gets its date mask from Text
File Input, d_select from a Select Values metadata change, and n_input a
zero-padded Integer mask. d_override has an explicit output format that must
win, d_default has no format anywhere and must keep the default date mask,
s_value is a plain String, and row 2 is all nulls.
0123-text-file-output-inherited-formats-read-back verifies the writ [...]
+ <extended_description/>
+ <created_user>-</created_user>
+ <modified_user>-</modified_user>
+ <created_date>2026/10/16 12:00:00.000</created_date>
+ <modified_date>2026/10/16 12:00:00.000</modified_date>
+ </info>
+ <transform>
+ <type>TextFileInput2</type>
+ <name>Read formatted values</name>
+ <file_type>CSV</file_type>
+ <compression>None</compression>
+ <separator>;</separator>
+ <enclosure>"</enclosure>
+ <enclosure_breaks>N</enclosure_breaks>
+ <escapechar/>
+ <header>Y</header>
+ <prependFileName>N</prependFileName>
+ <nr_headerlines>1</nr_headerlines>
+ <footer>N</footer>
+ <nr_footerlines>1</nr_footerlines>
+ <line_wrapped>N</line_wrapped>
+ <nr_wraps>1</nr_wraps>
+ <layout_paged>N</layout_paged>
+ <nr_lines_per_page>80</nr_lines_per_page>
+ <nr_lines_doc_header>0</nr_lines_doc_header>
+ <noempty>Y</noempty>
+ <include>N</include>
+ <include_field/>
+ <rownum>N</rownum>
+ <rownum_field/>
+ <rownumByFile>N</rownumByFile>
+ <format>mixed</format>
+ <encoding>UTF-8</encoding>
+ <limit>0</limit>
+ <date_format_lenient>N</date_format_lenient>
+ <date_format_locale>en_US</date_format_locale>
+ <length>Characters</length>
+ <filters/>
+ <error_count_field/>
+ <error_fields_field/>
+ <error_text_field/>
+ <error_line_skipped>N</error_line_skipped>
+ <schema_definition/>
+ <ignore_fields>N</ignore_fields>
+ <file>
+ <name>${PROJECT_HOME}/files/inherited-formats.csv</name>
+ <filemask/>
+ <exclude_filemask/>
+ <file_required>Y</file_required>
+ <include_subfolders>N</include_subfolders>
+ <type>CSV</type>
+ <compression>None</compression>
+ </file>
+ <files>
+ <file>
+ <name>${PROJECT_HOME}/files/inherited-formats.csv</name>
+ <filemask/>
+ <exclude_filemask/>
+ <file_required>Y</file_required>
+ <include_subfolders>N</include_subfolders>
+ </file>
+ </files>
+ <accept_filenames>N</accept_filenames>
+ <accept_transform_name/>
+ <passing_through_fields>N</passing_through_fields>
+ <accept_field/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <error_ignored>N</error_ignored>
+ <file_error_field/>
+ <file_error_message_field/>
+ <skip_bad_files>N</skip_bad_files>
+ <bad_line_files_destination_directory/>
+ <bad_line_files_extension>warning</bad_line_files_extension>
+ <error_line_files_destination_directory/>
+ <error_line_files_extension>error</error_line_files_extension>
+ <line_number_files_destination_directory/>
+ <line_number_files_extension>line</line_number_files_extension>
+ <fields>
+ <field>
+ <name>id</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Integer</type>
+ <ignore>N</ignore>
+ <format>#</format>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_input</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Date</type>
+ <ignore>N</ignore>
+ <format>dd/MM/yyyy</format>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_select</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Date</type>
+ <ignore>N</ignore>
+ <format>yyyy-MM-dd</format>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_override</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Date</type>
+ <ignore>N</ignore>
+ <format>dd/MM/yyyy</format>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>d_default</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Date</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>n_input</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>Integer</type>
+ <ignore>N</ignore>
+ <format>000000</format>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ <field>
+ <name>s_value</name>
+ <position>-1</position>
+ <length>-1</length>
+ <type>String</type>
+ <ignore>N</ignore>
+ <format/>
+ <trim_type>none</trim_type>
+ <precision>-1</precision>
+ <currency/>
+ <decimal/>
+ <group/>
+ <repeat>N</repeat>
+ <null_string/>
+ <if_null_value/>
+ </field>
+ </fields>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>128</xloc>
+ <yloc>96</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>SelectValues</type>
+ <name>Set d_select mask</name>
+ <fields>
+ <select_unspecified>N</select_unspecified>
+ <meta>
+ <name>d_select</name>
+ <rename>d_select</rename>
+ <type>Date</type>
+ <length>-2</length>
+ <precision>-2</precision>
+ <storage_type/>
+ <conversion_mask>yyyyMMdd</conversion_mask>
+ <date_format_lenient>N</date_format_lenient>
+ <date_format_locale/>
+ <date_format_timezone/>
+ <lenient_string_to_number>N</lenient_string_to_number>
+ <decimal_symbol/>
+ <grouping_symbol/>
+ <currency_symbol/>
+ <encoding/>
+ </meta>
+ </fields>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>320</xloc>
+ <yloc>96</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <transform>
+ <type>TextFileOutput</type>
+ <name>Write inherited formats</name>
+ <separator>;</separator>
+ <enclosure>"</enclosure>
+ <enclosure_forced>N</enclosure_forced>
+ <enclosure_fix_disabled>N</enclosure_fix_disabled>
+ <header>Y</header>
+ <footer>N</footer>
+ <format>UNIX</format>
+ <compression>None</compression>
+ <encoding>UTF-8</encoding>
+ <endedLine/>
+ <fileNameInField>N</fileNameInField>
+ <fileNameField/>
+ <create_parent_folder>Y</create_parent_folder>
+ <file>
+ <name>${java.io.tmpdir}/0123/inherited-formats-out</name>
+ <do_not_open_new_file_init>N</do_not_open_new_file_init>
+ <extension>csv</extension>
+ <append>N</append>
+ <split>N</split>
+ <haspartno>N</haspartno>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <SpecifyFormat>N</SpecifyFormat>
+ <date_time_format/>
+ <add_to_result_filenames>N</add_to_result_filenames>
+ <pad>N</pad>
+ <fast_dump>N</fast_dump>
+ <splitevery>0</splitevery>
+ </file>
+ <fields>
+ <field>
+ <name>id</name>
+ <type>Integer</type>
+ <format>#</format>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>d_input</name>
+ <type>Date</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>d_select</name>
+ <type>Date</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>d_override</name>
+ <type>Date</type>
+ <format>yyyy-MM-dd</format>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>d_default</name>
+ <type>Date</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>n_input</name>
+ <type>Integer</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ <field>
+ <name>s_value</name>
+ <type>String</type>
+ <format/>
+ <currency/>
+ <decimal/>
+ <group/>
+ <nullif/>
+ <trim_type>none</trim_type>
+ <length>-1</length>
+ <precision>-1</precision>
+ </field>
+ </fields>
+ <distribute>Y</distribute>
+ <copies>1</copies>
+ <GUI>
+ <xloc>512</xloc>
+ <yloc>96</yloc>
+ </GUI>
+ <description/>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ </transform>
+ <order>
+ <hop>
+ <from>Read formatted values</from>
+ <to>Set d_select mask</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>Set d_select mask</from>
+ <to>Write inherited formats</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+ <notepads/>
+ <attributes/>
+ <transform_error_handling/>
+</pipeline>
diff --git a/integration-tests/transforms/datasets/golden-inherited-formats.csv
b/integration-tests/transforms/datasets/golden-inherited-formats.csv
new file mode 100644
index 0000000000..b81ccae9d2
--- /dev/null
+++ b/integration-tests/transforms/datasets/golden-inherited-formats.csv
@@ -0,0 +1,3 @@
+id,d_input,d_select,d_override,d_default,n_input,s_value
+1,17/05/2024,20240517,2024-05-17,2024/05/17 00:00:00.000,000042,abc
+2,,,,,,
diff --git a/integration-tests/transforms/files/inherited-formats.csv
b/integration-tests/transforms/files/inherited-formats.csv
new file mode 100644
index 0000000000..73adb452cc
--- /dev/null
+++ b/integration-tests/transforms/files/inherited-formats.csv
@@ -0,0 +1,3 @@
+id;d_input;d_select;d_override;d_default;n_input;s_value
+1;17/05/2024;2024-05-17;17/05/2024;2024/05/17 00:00:00.000;000042;abc
+2;;;;;;
diff --git
a/integration-tests/transforms/main-0123-text-file-output-inherited-formats.hwf
b/integration-tests/transforms/main-0123-text-file-output-inherited-formats.hwf
new file mode 100644
index 0000000000..3bbea6ebd4
--- /dev/null
+++
b/integration-tests/transforms/main-0123-text-file-output-inherited-formats.hwf
@@ -0,0 +1,115 @@
+<?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-0123-text-file-output-inherited-formats</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2026/10/16 12:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/10/16 12:00:00.000</modified_date>
+ <parameters>
+ </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>
+ <xloc>96</xloc>
+ <yloc>80</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0123-text-file-output-inherited-formats</name>
+ <description/>
+ <type>PIPELINE</type>
+ <attributes/>
+
<filename>${PROJECT_HOME}/0123-text-file-output-inherited-formats.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>
+ <set_logfile>N</set_logfile>
+ <logfile/>
+ <logext/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <loglevel>Basic</loglevel>
+ <set_append_logfile>N</set_append_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <follow_abort_remote>N</follow_abort_remote>
+ <create_parent_folder>N</create_parent_folder>
+ <run_configuration>local</run_configuration>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <parallel>N</parallel>
+ <xloc>288</xloc>
+ <yloc>80</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0123-text-file-output-inherited-formats-read-back UNIT</name>
+ <description/>
+ <type>RunPipelineTests</type>
+ <attributes/>
+ <test_names>
+ <test_name>
+ <name>0123-text-file-output-inherited-formats-read-back UNIT</name>
+ </test_name>
+ </test_names>
+ <parallel>N</parallel>
+ <xloc>544</xloc>
+ <yloc>80</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>START</from>
+ <to>0123-text-file-output-inherited-formats</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>0123-text-file-output-inherited-formats</from>
+ <to>0123-text-file-output-inherited-formats-read-back UNIT</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ </hops>
+ <notepads>
+ </notepads>
+ <attributes/>
+</workflow>
diff --git
a/integration-tests/transforms/metadata/dataset/0108-data-set-output.json
b/integration-tests/transforms/metadata/dataset/0108-data-set-output.json
index 0df7b60e90..01ec30050f 100644
--- a/integration-tests/transforms/metadata/dataset/0108-data-set-output.json
+++ b/integration-tests/transforms/metadata/dataset/0108-data-set-output.json
@@ -1,5 +1,6 @@
{
"base_filename": "0108-data-set-output.csv",
+ "folder_name": "${PROJECT_HOME}/output/0108-data-set-output",
"name": "0108-data-set-output",
"description": "",
"dataset_fields": [
diff --git
a/integration-tests/transforms/metadata/dataset/golden-inherited-formats.json
b/integration-tests/transforms/metadata/dataset/golden-inherited-formats.json
new file mode 100644
index 0000000000..5f88bec321
--- /dev/null
+++
b/integration-tests/transforms/metadata/dataset/golden-inherited-formats.json
@@ -0,0 +1,63 @@
+{
+ "base_filename": "golden-inherited-formats.csv",
+ "name": "golden-inherited-formats",
+ "description": "Text written by Text File Output when blank output formats
inherit the incoming field masks.",
+ "dataset_fields": [
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "id",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "d_input",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "d_select",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "d_override",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "d_default",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "n_input",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 2,
+ "field_precision": -1,
+ "field_name": "s_value",
+ "field_format": ""
+ }
+ ]
+}
diff --git
a/integration-tests/transforms/metadata/unit-test/0123-text-file-output-inherited-formats-read-back
UNIT.json
b/integration-tests/transforms/metadata/unit-test/0123-text-file-output-inherited-formats-read-back
UNIT.json
new file mode 100644
index 0000000000..aab9a9e77e
--- /dev/null
+++
b/integration-tests/transforms/metadata/unit-test/0123-text-file-output-inherited-formats-read-back
UNIT.json
@@ -0,0 +1,52 @@
+{
+ "variableValues": [],
+ "database_replacements": [],
+ "autoOpening": false,
+ "basePath": "",
+ "golden_data_sets": [
+ {
+ "field_mappings": [
+ {
+ "transform_field": "id",
+ "data_set_field": "id"
+ },
+ {
+ "transform_field": "d_input",
+ "data_set_field": "d_input"
+ },
+ {
+ "transform_field": "d_select",
+ "data_set_field": "d_select"
+ },
+ {
+ "transform_field": "d_override",
+ "data_set_field": "d_override"
+ },
+ {
+ "transform_field": "d_default",
+ "data_set_field": "d_default"
+ },
+ {
+ "transform_field": "n_input",
+ "data_set_field": "n_input"
+ },
+ {
+ "transform_field": "s_value",
+ "data_set_field": "s_value"
+ }
+ ],
+ "field_order": [
+ "id"
+ ],
+ "transform_name": "OUTPUT",
+ "data_set_name": "golden-inherited-formats"
+ }
+ ],
+ "input_data_sets": [],
+ "name": "0123-text-file-output-inherited-formats-read-back UNIT",
+ "description": "Reads back the file written by
0123-text-file-output-inherited-formats as text and compares it to the expected
inherited formats",
+ "trans_test_tweaks": [],
+ "persist_filename": "",
+ "pipeline_filename":
"./0123-text-file-output-inherited-formats-read-back.hpl",
+ "test_type": "UNIT_TEST"
+}
diff --git
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputMeta.java
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputMeta.java
index 29a855691b..876f9d1094 100644
---
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputMeta.java
+++
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputMeta.java
@@ -473,7 +473,7 @@ public class TextFileOutputMeta extends
BaseTransformMeta<TextFileOutput, TextFi
if (v != null) {
v.setLength(field.getLength());
v.setPrecision(field.getPrecision());
- if (field.getFormat() != null) {
+ if (!Utils.isEmpty(field.getFormat())) {
v.setConversionMask(field.getFormat());
}
v.setDecimalSymbol(field.getDecimalSymbol());
diff --git
a/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputFormatInheritanceTest.java
b/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputFormatInheritanceTest.java
new file mode 100644
index 0000000000..9034a63f16
--- /dev/null
+++
b/plugins/transforms/textfile/src/test/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputFormatInheritanceTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.textfileoutput;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import org.apache.hop.core.HopEnvironment;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaDate;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.w3c.dom.Node;
+
+/**
+ * An output field with a blank Format must keep the conversion mask of the
incoming field, so a
+ * date formatted upstream is written with that format. Up to 2.17 a saved
{@code <format/>} loaded
+ * as null; since 2.18 it loads as an empty string, which must not wipe the
inherited mask.
+ */
+class TextFileOutputFormatInheritanceTest {
+ @RegisterExtension
+ static RestoreHopEngineEnvironmentExtension env = new
RestoreHopEngineEnvironmentExtension();
+
+ private static final String INPUT_MASK = "dd-MM-yyyy";
+
+ @BeforeAll
+ static void setUpBeforeClass() throws HopException {
+ HopEnvironment.init();
+ }
+
+ @Test
+ void blankFormatKeepsInputDateMask() throws Exception {
+ TextFileOutputMeta meta = loadMeta(fieldXml("<format/>"));
+
+ IRowMeta row = inputRowMeta();
+ meta.getFields(row, "Text file output", null, null, new Variables(), null);
+
+ IValueMeta date = row.searchValueMeta("date");
+ assertEquals(INPUT_MASK, date.getConversionMask());
+ assertEquals("17-05-2024", date.getString(may17()));
+ }
+
+ @Test
+ void specifiedFormatOverridesInputDateMask() throws Exception {
+ TextFileOutputMeta meta =
loadMeta(fieldXml("<format>yyyy/MM/dd</format>"));
+
+ IRowMeta row = inputRowMeta();
+ meta.getFields(row, "Text file output", null, null, new Variables(), null);
+
+ IValueMeta date = row.searchValueMeta("date");
+ assertEquals("yyyy/MM/dd", date.getConversionMask());
+ assertEquals("2024/05/17", date.getString(may17()));
+ }
+
+ private static String fieldXml(String formatTag) {
+ return "<transform><fields><field>"
+ + "<name>date</name>"
+ + "<type>Date</type>"
+ + formatTag
+ + "<currency/><decimal/><group/><nullif/>"
+ + "<trim_type>none</trim_type>"
+ + "<roundingType>half_even</roundingType>"
+ + "<length>-1</length><precision>-1</precision>"
+ + "</field></fields></transform>";
+ }
+
+ private static TextFileOutputMeta loadMeta(String xml) throws Exception {
+ Node transformNode = XmlHandler.getSubNode(XmlHandler.loadXmlString(xml),
"transform");
+ TextFileOutputMeta meta = new TextFileOutputMeta();
+ meta.loadXml(transformNode, new MemoryMetadataProvider());
+ return meta;
+ }
+
+ private static IRowMeta inputRowMeta() {
+ IValueMeta date = new ValueMetaDate("date");
+ date.setConversionMask(INPUT_MASK);
+ IRowMeta row = new RowMeta();
+ row.addValueMeta(date);
+ return row;
+ }
+
+ private static Date may17() {
+ return new GregorianCalendar(2024, Calendar.MAY, 17).getTime();
+ }
+}