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

hansva pushed a commit to branch 2.18.1-patch
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/2.18.1-patch by this push:
     new 8e363b0991 get file names type filter stopped working, fixes #7253 
(#7258)
8e363b0991 is described below

commit 8e363b09911c82f1177385d2fd6660eb3eaa07a2
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Jun 10 13:19:21 2026 +0200

    get file names type filter stopped working, fixes #7253 (#7258)
---
 .../apache/hop/core/fileinput/FileInputList.java   |  12 +-
 .../hop/core/fileinput/FileInputListTest.java      | 187 ++++++++++++
 .../transforms/0038-getfilenames-excludemask.hpl   | 279 +++++++++++++++++
 .../0038-getfilenames-filesandfolders.hpl          | 212 +++++++++++++
 .../0038-getfilenames-onlyfiles-subfolders.hpl     | 279 +++++++++++++++++
 .../transforms/0038-getfilenames-onlyfiles.hpl     | 279 +++++++++++++++++
 .../transforms/0038-getfilenames-onlyfolders.hpl   | 279 +++++++++++++++++
 .../transforms/files/getfilenames-tree/data.csv    |   2 +
 .../transforms/files/getfilenames-tree/file-a.txt  |   1 +
 .../transforms/files/getfilenames-tree/file-b.txt  |   1 +
 .../getfilenames-tree/folder-one/nested-1.txt      |   1 +
 .../getfilenames-tree/folder-two/nested-2.txt      |   1 +
 .../transforms/main-0038-getfilename.hwf           | 340 +++++++++++++++------
 .../getfilenames/GetFileNamesTransformTest.java    | 162 ++++++++++
 14 files changed, 1943 insertions(+), 92 deletions(-)

diff --git 
a/core/src/main/java/org/apache/hop/core/fileinput/FileInputList.java 
b/core/src/main/java/org/apache/hop/core/fileinput/FileInputList.java
index 4e73e8948a..4855ffe2fd 100644
--- a/core/src/main/java/org/apache/hop/core/fileinput/FileInputList.java
+++ b/core/src/main/java/org/apache/hop/core/fileinput/FileInputList.java
@@ -81,16 +81,20 @@ public class FileInputList {
       IVariables variables, List<InputFile> inputFiles, FileTypeFilter[] 
fileTypeFilters) {
     FileInputList fileInputList = new FileInputList();
 
-    for (InputFile inputFile : inputFiles) {
+    for (int i = 0; i < inputFiles.size(); i++) {
+      final InputFile inputFile = inputFiles.get(i);
       final String oneFile = variables.resolve(inputFile.getFileName());
       final String oneMask = variables.resolve(inputFile.getFileMask());
       final String excludeOneMask = 
variables.resolve(inputFile.getExcludeFileMask());
       final boolean oneRequired = inputFile.isFileRequired();
       final boolean subDirs = inputFile.isIncludeSubFolders();
+
+      FileTypeFilter selectedFilter = inputFile.getFileTypeFilter();
+      if (fileTypeFilters != null && i < fileTypeFilters.length && 
fileTypeFilters[i] != null) {
+        selectedFilter = fileTypeFilters[i];
+      }
       final FileTypeFilter filter =
-          ((fileTypeFilters == null || inputFile.getFileTypeFilter() == null)
-              ? FileTypeFilter.ONLY_FILES
-              : inputFile.getFileTypeFilter());
+          selectedFilter == null ? FileTypeFilter.ONLY_FILES : selectedFilter;
 
       if (Utils.isEmpty(oneFile)) {
         continue;
diff --git 
a/core/src/test/java/org/apache/hop/core/fileinput/FileInputListTest.java 
b/core/src/test/java/org/apache/hop/core/fileinput/FileInputListTest.java
index f0a9be3143..f84e68a610 100644
--- a/core/src/test/java/org/apache/hop/core/fileinput/FileInputListTest.java
+++ b/core/src/test/java/org/apache/hop/core/fileinput/FileInputListTest.java
@@ -17,14 +17,71 @@
 package org.apache.hop.core.fileinput;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.commons.vfs2.FileObject;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.variables.Variables;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
 class FileInputListTest {
 
+  /**
+   * Builds a deterministic tree used by the filter/option tests:
+   *
+   * <pre>
+   *   root/
+   *     file-a.txt
+   *     file-b.txt
+   *     data.csv
+   *     folder-one/nested-1.txt
+   *     folder-two/nested-2.txt
+   * </pre>
+   */
+  private static void buildTree(Path root) throws IOException {
+    Files.createDirectories(root.resolve("folder-one"));
+    Files.createDirectories(root.resolve("folder-two"));
+    Files.writeString(root.resolve("file-a.txt"), "alpha");
+    Files.writeString(root.resolve("file-b.txt"), "beta");
+    Files.writeString(root.resolve("data.csv"), "c,d");
+    Files.writeString(root.resolve("folder-one/nested-1.txt"), "keep");
+    Files.writeString(root.resolve("folder-two/nested-2.txt"), "keep");
+  }
+
+  private static FileInputList list(
+      IVariables variables,
+      Path root,
+      FileTypeFilter filter,
+      String mask,
+      String excludeMask,
+      boolean subFolders) {
+    return FileInputList.createFileList(
+        variables,
+        new String[] {root.toAbsolutePath().toString()},
+        new String[] {mask},
+        new String[] {excludeMask},
+        new String[] {"N"},
+        new boolean[] {subFolders},
+        new FileTypeFilter[] {filter});
+  }
+
+  private static List<String> baseNames(FileInputList list) {
+    List<String> baseNames = new ArrayList<>();
+    for (FileObject file : list.getFiles()) {
+      baseNames.add(file.getName().getBaseName());
+    }
+    return baseNames;
+  }
+
   @Test
   void testGetUrlStrings() {
 
@@ -45,4 +102,134 @@ class FileInputListTest {
     assertEquals(sFileA, result[0]);
     assertEquals(sFileB, result[1]);
   }
+
+  /**
+   * Regression test for the "only folders" file type filter being silently 
downgraded to "only
+   * files". The filter is carried on each {@link InputFile} (it is dropped 
from the {@code
+   * FileTypeFilter[]} argument when the list is built from the String[] 
overloads used by Get File
+   * Names), so it has to be honored even when that array is null. Before the 
fix only the plain
+   * file was returned instead of the sub-folder.
+   */
+  @Test
+  void onlyFoldersFilterIsHonoredThroughStringArrayOverload(@TempDir Path 
tempDir)
+      throws IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, FileTypeFilter.ONLY_FOLDERS, 
"", "", false));
+    assertEquals(2, names.size(), "Expected only the sub-folders, got: " + 
names);
+    assertTrue(names.contains("folder-one") && names.contains("folder-two"));
+    assertFalse(names.contains("file-a.txt"), "A file leaked through the 
only-folders filter");
+  }
+
+  @Test
+  void onlyFilesFilterIsHonoredThroughStringArrayOverload(@TempDir Path 
tempDir)
+      throws IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, FileTypeFilter.ONLY_FILES, 
"", "", false));
+    assertEquals(3, names.size(), "Expected only the top level files, got: " + 
names);
+    assertTrue(
+        names.contains("file-a.txt") && names.contains("file-b.txt") && 
names.contains("data.csv"));
+    assertFalse(names.contains("folder-one"));
+  }
+
+  @Test
+  void filesAndFoldersFilterReturnsBothThroughStringArrayOverload(@TempDir 
Path tempDir)
+      throws IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, 
FileTypeFilter.FILES_AND_FOLDERS, "", "", false));
+    assertEquals(5, names.size(), "Expected the files and the folders, got: " 
+ names);
+  }
+
+  @Test
+  void includeMaskMatchesOnlyTxtFiles(@TempDir Path tempDir) throws 
IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, FileTypeFilter.ONLY_FILES, 
".*\\.txt", "", false));
+    assertEquals(2, names.size(), "Expected the two .txt files, got: " + 
names);
+    assertTrue(names.contains("file-a.txt") && names.contains("file-b.txt"));
+    assertFalse(names.contains("data.csv"));
+  }
+
+  @Test
+  void excludeMaskRemovesMatchingFiles(@TempDir Path tempDir) throws 
IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, FileTypeFilter.ONLY_FILES, 
"", ".*\\.csv", false));
+    assertEquals(2, names.size(), "Expected the csv to be excluded, got: " + 
names);
+    assertFalse(names.contains("data.csv"));
+  }
+
+  @Test
+  void includeSubFoldersWalksNestedFiles(@TempDir Path tempDir) throws 
IOException {
+    buildTree(tempDir);
+    List<String> names =
+        baseNames(list(new Variables(), tempDir, FileTypeFilter.ONLY_FILES, 
"", "", true));
+    assertEquals(5, names.size(), "Expected top level and nested files, got: " 
+ names);
+    assertTrue(names.contains("nested-1.txt") && 
names.contains("nested-2.txt"));
+  }
+
+  @Test
+  void requiredMissingFolderIsReportedAsNonExistent(@TempDir Path tempDir) {
+    IVariables variables = new Variables();
+    FileInputList list =
+        FileInputList.createFileList(
+            variables,
+            new String[] 
{tempDir.resolve("does-not-exist").toAbsolutePath().toString()},
+            new String[] {""},
+            new String[] {""},
+            new String[] {"Y"},
+            new boolean[] {false},
+            new FileTypeFilter[] {FileTypeFilter.ONLY_FILES});
+    assertEquals(0, list.nrOfFiles());
+    assertEquals(1, list.getNonExistentFiles().size());
+    assertEquals(1, list.nrOfMissingFiles());
+  }
+
+  @Test
+  void blankFileNameIsSkipped() {
+    IVariables variables = new Variables();
+    FileInputList list =
+        FileInputList.createFileList(
+            variables,
+            new String[] {""},
+            new String[] {""},
+            new String[] {""},
+            new String[] {"N"},
+            new boolean[] {false},
+            new FileTypeFilter[] {FileTypeFilter.ONLY_FILES});
+    assertEquals(0, list.nrOfFiles());
+    assertEquals(0, list.nrOfMissingFiles());
+  }
+
+  @Test
+  void nullFilterArrayDefaultsToOnlyFiles(@TempDir Path tempDir) throws 
IOException {
+    buildTree(tempDir);
+    // No filter information at all (array null, InputFile filter null) must 
default to files only.
+    IVariables variables = new Variables();
+    FileInputList list =
+        FileInputList.createFileList(
+            variables,
+            new String[] {tempDir.toAbsolutePath().toString()},
+            new String[] {""},
+            new String[] {""},
+            new String[] {"N"},
+            new boolean[] {false});
+    List<String> names = baseNames(list);
+    assertEquals(3, names.size(), "Default (no filter) should return files 
only, got: " + names);
+    assertFalse(names.contains("folder-one"));
+  }
+
+  @Test
+  void createFolderListReturnsOnlySubFolders(@TempDir Path tempDir) throws 
IOException {
+    buildTree(tempDir);
+    IVariables variables = new Variables();
+    FileInputList list =
+        FileInputList.createFolderList(
+            variables, new String[] {tempDir.toAbsolutePath().toString()}, new 
String[] {"N"});
+    List<String> names = baseNames(list);
+    assertEquals(2, names.size(), "createFolderList should return only 
folders, got: " + names);
+    assertTrue(names.contains("folder-one") && names.contains("folder-two"));
+  }
 }
diff --git a/integration-tests/transforms/0038-getfilenames-excludemask.hpl 
b/integration-tests/transforms/0038-getfilenames-excludemask.hpl
new file mode 100644
index 0000000000..52a6ffd8d1
--- /dev/null
+++ b/integration-tests/transforms/0038-getfilenames-excludemask.hpl
@@ -0,0 +1,279 @@
+<?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>0038-getfilenames-excludemask</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/06/10 12:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/06/10 12:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Get file names</from>
+      <to>Keep type</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Wrong type - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Count rows</from>
+      <to>Check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Wrong count - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Get file names</name>
+    <type>GetFileNames</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <isaddresult>N</isaddresult>
+    <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <exclude_wildcard_Field/>
+    <filename_Field/>
+    <dynamic_include_subfolders>N</dynamic_include_subfolders>
+    <wildcard_Field/>
+    <filefield>N</filefield>
+    <file>
+      <exclude_filemask>.*\.csv</exclude_filemask>
+      <filemask></filemask>
+      <name>${PROJECT_HOME}/files/getfilenames-tree</name>
+      <file_required>N</file_required>
+      <include_subfolders>N</include_subfolders>
+    </file>
+    <filter>
+      <filterfiletype>only_files</filterfiletype>
+    </filter>
+    <rownum>N</rownum>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <limit>0</limit>
+    <rownum_field/>
+    <attributes/>
+    <GUI>
+      <xloc>192</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Keep type</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Count rows</send_true_to>
+    <send_false_to>Wrong type - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>type</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>String</type>
+          <text>file</text>
+          <length>-1</length>
+          <precision>-1</precision>
+          <isnull>N</isnull>
+          <mask/>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong type - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected file type returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <all_rows>N</all_rows>
+    <ignore_aggregate>N</ignore_aggregate>
+    <field_ignore/>
+    <directory>${java.io.tmpdir}</directory>
+    <prefix>grp</prefix>
+    <add_linenr>N</add_linenr>
+    <linenr_fieldname/>
+    <give_back_row>N</give_back_row>
+    <group>
+    </group>
+    <fields>
+      <field>
+        <aggregate>num_file</aggregate>
+        <subject>filename</subject>
+        <type>COUNT_ANY</type>
+        <valuefield/>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>512</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Success</send_true_to>
+    <send_false_to>Wrong count - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>num_file</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>Integer</type>
+          <text>2</text>
+          <length>-1</length>
+          <precision>0</precision>
+          <isnull>N</isnull>
+          <mask>####0;-####0</mask>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong count - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected number of rows returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>832</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/0038-getfilenames-filesandfolders.hpl 
b/integration-tests/transforms/0038-getfilenames-filesandfolders.hpl
new file mode 100644
index 0000000000..bcacfe81dd
--- /dev/null
+++ b/integration-tests/transforms/0038-getfilenames-filesandfolders.hpl
@@ -0,0 +1,212 @@
+<?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>0038-getfilenames-filesandfolders</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/06/10 12:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/06/10 12:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Get file names</from>
+      <to>Count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Count rows</from>
+      <to>Check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Wrong count - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Get file names</name>
+    <type>GetFileNames</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <isaddresult>N</isaddresult>
+    <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <exclude_wildcard_Field/>
+    <filename_Field/>
+    <dynamic_include_subfolders>N</dynamic_include_subfolders>
+    <wildcard_Field/>
+    <filefield>N</filefield>
+    <file>
+      <exclude_filemask></exclude_filemask>
+      <filemask></filemask>
+      <name>${PROJECT_HOME}/files/getfilenames-tree</name>
+      <file_required>N</file_required>
+      <include_subfolders>N</include_subfolders>
+    </file>
+    <filter>
+      <filterfiletype>all_files</filterfiletype>
+    </filter>
+    <rownum>N</rownum>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <limit>0</limit>
+    <rownum_field/>
+    <attributes/>
+    <GUI>
+      <xloc>192</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <all_rows>N</all_rows>
+    <ignore_aggregate>N</ignore_aggregate>
+    <field_ignore/>
+    <directory>${java.io.tmpdir}</directory>
+    <prefix>grp</prefix>
+    <add_linenr>N</add_linenr>
+    <linenr_fieldname/>
+    <give_back_row>N</give_back_row>
+    <group>
+    </group>
+    <fields>
+      <field>
+        <aggregate>num_file</aggregate>
+        <subject>filename</subject>
+        <type>COUNT_ANY</type>
+        <valuefield/>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>512</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Success</send_true_to>
+    <send_false_to>Wrong count - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>num_file</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>Integer</type>
+          <text>5</text>
+          <length>-1</length>
+          <precision>0</precision>
+          <isnull>N</isnull>
+          <mask>####0;-####0</mask>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong count - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected number of rows returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>832</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git 
a/integration-tests/transforms/0038-getfilenames-onlyfiles-subfolders.hpl 
b/integration-tests/transforms/0038-getfilenames-onlyfiles-subfolders.hpl
new file mode 100644
index 0000000000..ac708e8a10
--- /dev/null
+++ b/integration-tests/transforms/0038-getfilenames-onlyfiles-subfolders.hpl
@@ -0,0 +1,279 @@
+<?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>0038-getfilenames-onlyfiles-subfolders</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/06/10 12:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/06/10 12:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Get file names</from>
+      <to>Keep type</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Wrong type - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Count rows</from>
+      <to>Check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Wrong count - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Get file names</name>
+    <type>GetFileNames</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <isaddresult>N</isaddresult>
+    <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <exclude_wildcard_Field/>
+    <filename_Field/>
+    <dynamic_include_subfolders>N</dynamic_include_subfolders>
+    <wildcard_Field/>
+    <filefield>N</filefield>
+    <file>
+      <exclude_filemask></exclude_filemask>
+      <filemask></filemask>
+      <name>${PROJECT_HOME}/files/getfilenames-tree</name>
+      <file_required>N</file_required>
+      <include_subfolders>Y</include_subfolders>
+    </file>
+    <filter>
+      <filterfiletype>only_files</filterfiletype>
+    </filter>
+    <rownum>N</rownum>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <limit>0</limit>
+    <rownum_field/>
+    <attributes/>
+    <GUI>
+      <xloc>192</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Keep type</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Count rows</send_true_to>
+    <send_false_to>Wrong type - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>type</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>String</type>
+          <text>file</text>
+          <length>-1</length>
+          <precision>-1</precision>
+          <isnull>N</isnull>
+          <mask/>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong type - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected file type returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <all_rows>N</all_rows>
+    <ignore_aggregate>N</ignore_aggregate>
+    <field_ignore/>
+    <directory>${java.io.tmpdir}</directory>
+    <prefix>grp</prefix>
+    <add_linenr>N</add_linenr>
+    <linenr_fieldname/>
+    <give_back_row>N</give_back_row>
+    <group>
+    </group>
+    <fields>
+      <field>
+        <aggregate>num_file</aggregate>
+        <subject>filename</subject>
+        <type>COUNT_ANY</type>
+        <valuefield/>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>512</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Success</send_true_to>
+    <send_false_to>Wrong count - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>num_file</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>Integer</type>
+          <text>5</text>
+          <length>-1</length>
+          <precision>0</precision>
+          <isnull>N</isnull>
+          <mask>####0;-####0</mask>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong count - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected number of rows returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>832</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/0038-getfilenames-onlyfiles.hpl 
b/integration-tests/transforms/0038-getfilenames-onlyfiles.hpl
new file mode 100644
index 0000000000..cad58068a8
--- /dev/null
+++ b/integration-tests/transforms/0038-getfilenames-onlyfiles.hpl
@@ -0,0 +1,279 @@
+<?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>0038-getfilenames-onlyfiles</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/06/10 12:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/06/10 12:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Get file names</from>
+      <to>Keep type</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Wrong type - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Count rows</from>
+      <to>Check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Wrong count - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Get file names</name>
+    <type>GetFileNames</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <isaddresult>N</isaddresult>
+    <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <exclude_wildcard_Field/>
+    <filename_Field/>
+    <dynamic_include_subfolders>N</dynamic_include_subfolders>
+    <wildcard_Field/>
+    <filefield>N</filefield>
+    <file>
+      <exclude_filemask></exclude_filemask>
+      <filemask></filemask>
+      <name>${PROJECT_HOME}/files/getfilenames-tree</name>
+      <file_required>N</file_required>
+      <include_subfolders>N</include_subfolders>
+    </file>
+    <filter>
+      <filterfiletype>only_files</filterfiletype>
+    </filter>
+    <rownum>N</rownum>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <limit>0</limit>
+    <rownum_field/>
+    <attributes/>
+    <GUI>
+      <xloc>192</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Keep type</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Count rows</send_true_to>
+    <send_false_to>Wrong type - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>type</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>String</type>
+          <text>file</text>
+          <length>-1</length>
+          <precision>-1</precision>
+          <isnull>N</isnull>
+          <mask/>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong type - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected file type returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <all_rows>N</all_rows>
+    <ignore_aggregate>N</ignore_aggregate>
+    <field_ignore/>
+    <directory>${java.io.tmpdir}</directory>
+    <prefix>grp</prefix>
+    <add_linenr>N</add_linenr>
+    <linenr_fieldname/>
+    <give_back_row>N</give_back_row>
+    <group>
+    </group>
+    <fields>
+      <field>
+        <aggregate>num_file</aggregate>
+        <subject>filename</subject>
+        <type>COUNT_ANY</type>
+        <valuefield/>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>512</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Success</send_true_to>
+    <send_false_to>Wrong count - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>num_file</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>Integer</type>
+          <text>3</text>
+          <length>-1</length>
+          <precision>0</precision>
+          <isnull>N</isnull>
+          <mask>####0;-####0</mask>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong count - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected number of rows returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>832</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/0038-getfilenames-onlyfolders.hpl 
b/integration-tests/transforms/0038-getfilenames-onlyfolders.hpl
new file mode 100644
index 0000000000..43dd8d0336
--- /dev/null
+++ b/integration-tests/transforms/0038-getfilenames-onlyfolders.hpl
@@ -0,0 +1,279 @@
+<?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>0038-getfilenames-onlyfolders</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/06/10 12:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/06/10 12:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Get file names</from>
+      <to>Keep type</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Count rows</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Keep type</from>
+      <to>Wrong type - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Count rows</from>
+      <to>Check count</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Success</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Check count</from>
+      <to>Wrong count - Abort</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Get file names</name>
+    <type>GetFileNames</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <isaddresult>N</isaddresult>
+    <doNotFailIfNoFile>N</doNotFailIfNoFile>
+    <exclude_wildcard_Field/>
+    <filename_Field/>
+    <dynamic_include_subfolders>N</dynamic_include_subfolders>
+    <wildcard_Field/>
+    <filefield>N</filefield>
+    <file>
+      <exclude_filemask></exclude_filemask>
+      <filemask></filemask>
+      <name>${PROJECT_HOME}/files/getfilenames-tree</name>
+      <file_required>N</file_required>
+      <include_subfolders>N</include_subfolders>
+    </file>
+    <filter>
+      <filterfiletype>only_folders</filterfiletype>
+    </filter>
+    <rownum>N</rownum>
+    <raiseAnExceptionIfNoFile>N</raiseAnExceptionIfNoFile>
+    <limit>0</limit>
+    <rownum_field/>
+    <attributes/>
+    <GUI>
+      <xloc>192</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Keep type</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Count rows</send_true_to>
+    <send_false_to>Wrong type - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>type</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>String</type>
+          <text>folder</text>
+          <length>-1</length>
+          <precision>-1</precision>
+          <isnull>N</isnull>
+          <mask/>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong type - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected file type returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>352</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Count rows</name>
+    <type>GroupBy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <all_rows>N</all_rows>
+    <ignore_aggregate>N</ignore_aggregate>
+    <field_ignore/>
+    <directory>${java.io.tmpdir}</directory>
+    <prefix>grp</prefix>
+    <add_linenr>N</add_linenr>
+    <linenr_fieldname/>
+    <give_back_row>N</give_back_row>
+    <group>
+    </group>
+    <fields>
+      <field>
+        <aggregate>num_file</aggregate>
+        <subject>filename</subject>
+        <type>COUNT_ANY</type>
+        <valuefield/>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>512</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Check count</name>
+    <type>FilterRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <send_true_to>Success</send_true_to>
+    <send_false_to>Wrong count - Abort</send_false_to>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>num_file</leftvalue>
+        <function>=</function>
+        <rightvalue/>
+        <value>
+          <name>constant</name>
+          <type>Integer</type>
+          <text>2</text>
+          <length>-1</length>
+          <precision>0</precision>
+          <isnull>N</isnull>
+          <mask>####0;-####0</mask>
+        </value>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Wrong count - Abort</name>
+    <type>Abort</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <abort_option>ABORT_WITH_ERROR</abort_option>
+    <always_log_rows>Y</always_log_rows>
+    <message>Unexpected number of rows returned by Get file names</message>
+    <row_threshold>0</row_threshold>
+    <attributes/>
+    <GUI>
+      <xloc>672</xloc>
+      <yloc>256</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Success</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>832</xloc>
+      <yloc>96</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/integration-tests/transforms/files/getfilenames-tree/data.csv 
b/integration-tests/transforms/files/getfilenames-tree/data.csv
new file mode 100644
index 0000000000..f4238c901c
--- /dev/null
+++ b/integration-tests/transforms/files/getfilenames-tree/data.csv
@@ -0,0 +1,2 @@
+c,d
+1,2
diff --git a/integration-tests/transforms/files/getfilenames-tree/file-a.txt 
b/integration-tests/transforms/files/getfilenames-tree/file-a.txt
new file mode 100644
index 0000000000..4a58007052
--- /dev/null
+++ b/integration-tests/transforms/files/getfilenames-tree/file-a.txt
@@ -0,0 +1 @@
+alpha
diff --git a/integration-tests/transforms/files/getfilenames-tree/file-b.txt 
b/integration-tests/transforms/files/getfilenames-tree/file-b.txt
new file mode 100644
index 0000000000..65b2df87f7
--- /dev/null
+++ b/integration-tests/transforms/files/getfilenames-tree/file-b.txt
@@ -0,0 +1 @@
+beta
diff --git 
a/integration-tests/transforms/files/getfilenames-tree/folder-one/nested-1.txt 
b/integration-tests/transforms/files/getfilenames-tree/folder-one/nested-1.txt
new file mode 100644
index 0000000000..2fa992c0b8
--- /dev/null
+++ 
b/integration-tests/transforms/files/getfilenames-tree/folder-one/nested-1.txt
@@ -0,0 +1 @@
+keep
diff --git 
a/integration-tests/transforms/files/getfilenames-tree/folder-two/nested-2.txt 
b/integration-tests/transforms/files/getfilenames-tree/folder-two/nested-2.txt
new file mode 100644
index 0000000000..2fa992c0b8
--- /dev/null
+++ 
b/integration-tests/transforms/files/getfilenames-tree/folder-two/nested-2.txt
@@ -0,0 +1 @@
+keep
diff --git a/integration-tests/transforms/main-0038-getfilename.hwf 
b/integration-tests/transforms/main-0038-getfilename.hwf
index 0b6608b0a7..8c160db50d 100644
--- a/integration-tests/transforms/main-0038-getfilename.hwf
+++ b/integration-tests/transforms/main-0038-getfilename.hwf
@@ -22,172 +22,302 @@ limitations under the License.
   <name_sync_with_filename>Y</name_sync_with_filename>
   <description/>
   <extended_description/>
-  <workflow_version/>
   <created_user>-</created_user>
-  <created_date>2021/12/14 22:45:32.205</created_date>
   <modified_user>-</modified_user>
+  <created_date>2021/12/14 22:45:32.205</created_date>
   <modified_date>2021/12/14 22:45:32.205</modified_date>
-  <parameters>
-    </parameters>
+  <workflow_version/>
+  <parameters/>
   <actions>
     <action>
+      <repeat>N</repeat>
+      <schedulerType>0</schedulerType>
+      <intervalSeconds>0</intervalSeconds>
+      <intervalMinutes>60</intervalMinutes>
+      <DayOfMonth>1</DayOfMonth>
+      <weekDay>1</weekDay>
+      <minutes>0</minutes>
+      <hour>12</hour>
+      <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
       <name>Start</name>
       <description/>
       <type>SPECIAL</type>
       <attributes/>
-      <DayOfMonth>1</DayOfMonth>
-      <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>128</xloc>
+      <xloc>64</xloc>
       <yloc>128</yloc>
+      <parallel>N</parallel>
       <attributes_hac/>
     </action>
     <action>
+      <filename>${PROJECT_HOME}/0038-getfilenames-basic.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>0038-getfilenames-basic.hpl</name>
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <clear_files>N</clear_files>
+      <xloc>240</xloc>
+      <yloc>128</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <filename>${PROJECT_HOME}/0038-getfilenames-fromfield.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>
-      <exec_per_row>N</exec_per_row>
-      <filename>${PROJECT_HOME}/0038-getfilenames-basic.hpl</filename>
+      <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>
-      <params_from_previous>N</params_from_previous>
       <run_configuration>local</run_configuration>
-      <set_append_logfile>N</set_append_logfile>
-      <set_logfile>N</set_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <parallel>N</parallel>
-      <xloc>304</xloc>
+      <name>0038-getfilenames-fromfield.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>544</xloc>
       <yloc>128</yloc>
+      <parallel>N</parallel>
       <attributes_hac/>
     </action>
     <action>
-      <name>0038-getfilenames-fromfield.hpl</name>
+      <filename>${PROJECT_HOME}/0038-getfilenames-regexp.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>0038-getfilenames-regexp.hpl</name>
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <clear_files>N</clear_files>
+      <xloc>832</xloc>
+      <yloc>128</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      
<filename>${PROJECT_HOME}/0038-getfilenames-nofiles-exception.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>
-      <exec_per_row>N</exec_per_row>
-      <filename>${PROJECT_HOME}/0038-getfilenames-fromfield.hpl</filename>
+      <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>
-      <params_from_previous>N</params_from_previous>
       <run_configuration>local</run_configuration>
-      <set_append_logfile>N</set_append_logfile>
-      <set_logfile>N</set_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <parallel>N</parallel>
-      <xloc>480</xloc>
+      <name>0038-getfilenames-nofiles-exception.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>1168</xloc>
       <yloc>128</yloc>
+      <parallel>N</parallel>
       <attributes_hac/>
     </action>
     <action>
-      <name>0038-getfilenames-regexp.hpl</name>
+      <name>Success</name>
       <description/>
-      <type>PIPELINE</type>
+      <type>SUCCESS</type>
       <attributes/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <clear_files>N</clear_files>
+      <xloc>1664</xloc>
+      <yloc>288</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <filename>${PROJECT_HOME}/0038-getfilenames-limit.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>
-      <exec_per_row>N</exec_per_row>
-      <filename>${PROJECT_HOME}/0038-getfilenames-regexp.hpl</filename>
+      <set_logfile>N</set_logfile>
+      <set_append_logfile>N</set_append_logfile>
+      <logfile/>
+      <logext/>
+      <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>
-      <params_from_previous>N</params_from_previous>
       <run_configuration>local</run_configuration>
-      <set_append_logfile>N</set_append_logfile>
-      <set_logfile>N</set_logfile>
-      <wait_until_finished>Y</wait_until_finished>
-      <parallel>N</parallel>
-      <xloc>656</xloc>
+      <name>0038-getfilenames-limit.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>1488</xloc>
       <yloc>128</yloc>
+      <parallel>N</parallel>
       <attributes_hac/>
     </action>
     <action>
-      <name>0038-getfilenames-nofiles-exception.hpl</name>
+      <filename>${PROJECT_HOME}/0038-getfilenames-onlyfolders.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>0038-getfilenames-onlyfolders.hpl</name>
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <clear_files>N</clear_files>
+      <xloc>240</xloc>
+      <yloc>288</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <filename>${PROJECT_HOME}/0038-getfilenames-onlyfiles.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>
-      <exec_per_row>N</exec_per_row>
-      
<filename>${PROJECT_HOME}/0038-getfilenames-nofiles-exception.hpl</filename>
+      <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>
-      <params_from_previous>N</params_from_previous>
       <run_configuration>local</run_configuration>
-      <set_append_logfile>N</set_append_logfile>
-      <set_logfile>N</set_logfile>
-      <wait_until_finished>Y</wait_until_finished>
+      <name>0038-getfilenames-onlyfiles.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>560</xloc>
+      <yloc>288</yloc>
       <parallel>N</parallel>
-      <xloc>864</xloc>
-      <yloc>128</yloc>
       <attributes_hac/>
     </action>
     <action>
-      <name>Success</name>
+      
<filename>${PROJECT_HOME}/0038-getfilenames-filesandfolders.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>0038-getfilenames-filesandfolders.hpl</name>
       <description/>
-      <type>SUCCESS</type>
+      <type>PIPELINE</type>
       <attributes/>
+      <xloc>832</xloc>
+      <yloc>288</yloc>
       <parallel>N</parallel>
-      <xloc>1248</xloc>
-      <yloc>128</yloc>
       <attributes_hac/>
     </action>
     <action>
-      <name>0038-getfilenames-limit.hpl</name>
+      
<filename>${PROJECT_HOME}/0038-getfilenames-onlyfiles-subfolders.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>0038-getfilenames-onlyfiles-subfolders.hpl</name>
       <description/>
       <type>PIPELINE</type>
       <attributes/>
-      <add_date>N</add_date>
-      <add_time>N</add_time>
-      <clear_files>N</clear_files>
+      <xloc>1152</xloc>
+      <yloc>288</yloc>
+      <parallel>N</parallel>
+      <attributes_hac/>
+    </action>
+    <action>
+      <filename>${PROJECT_HOME}/0038-getfilenames-excludemask.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>
-      <exec_per_row>N</exec_per_row>
-      <filename>${PROJECT_HOME}/0038-getfilenames-limit.hpl</filename>
-      <logext/>
-      <logfile/>
+      <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>
-      <params_from_previous>N</params_from_previous>
       <run_configuration>local</run_configuration>
-      <set_append_logfile>N</set_append_logfile>
-      <set_logfile>N</set_logfile>
-      <wait_until_finished>Y</wait_until_finished>
+      <name>0038-getfilenames-excludemask.hpl</name>
+      <description/>
+      <type>PIPELINE</type>
+      <attributes/>
+      <xloc>1472</xloc>
+      <yloc>288</yloc>
       <parallel>N</parallel>
-      <xloc>1040</xloc>
-      <yloc>128</yloc>
       <attributes_hac/>
     </action>
   </actions>
@@ -195,47 +325,81 @@ limitations under the License.
     <hop>
       <from>Start</from>
       <to>0038-getfilenames-basic.hpl</to>
-      <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>Y</unconditional>
+      <enabled>Y</enabled>
     </hop>
     <hop>
       <from>0038-getfilenames-basic.hpl</from>
       <to>0038-getfilenames-fromfield.hpl</to>
-      <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
+      <enabled>Y</enabled>
     </hop>
     <hop>
       <from>0038-getfilenames-fromfield.hpl</from>
       <to>0038-getfilenames-regexp.hpl</to>
-      <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
+      <enabled>Y</enabled>
     </hop>
     <hop>
       <from>0038-getfilenames-regexp.hpl</from>
       <to>0038-getfilenames-nofiles-exception.hpl</to>
-      <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
+      <enabled>Y</enabled>
     </hop>
     <hop>
       <from>0038-getfilenames-nofiles-exception.hpl</from>
       <to>0038-getfilenames-limit.hpl</to>
-      <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
+      <enabled>Y</enabled>
     </hop>
     <hop>
       <from>0038-getfilenames-limit.hpl</from>
-      <to>Success</to>
+      <to>0038-getfilenames-onlyfolders.hpl</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0038-getfilenames-onlyfolders.hpl</from>
+      <to>0038-getfilenames-onlyfiles.hpl</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0038-getfilenames-onlyfiles.hpl</from>
+      <to>0038-getfilenames-filesandfolders.hpl</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0038-getfilenames-filesandfolders.hpl</from>
+      <to>0038-getfilenames-onlyfiles-subfolders.hpl</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
       <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0038-getfilenames-onlyfiles-subfolders.hpl</from>
+      <to>0038-getfilenames-excludemask.hpl</to>
       <evaluation>Y</evaluation>
       <unconditional>N</unconditional>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>0038-getfilenames-excludemask.hpl</from>
+      <to>Success</to>
+      <evaluation>Y</evaluation>
+      <unconditional>N</unconditional>
+      <enabled>Y</enabled>
     </hop>
   </hops>
-  <notepads>
-  </notepads>
+  <notepads/>
   <attributes/>
 </workflow>
diff --git 
a/plugins/transforms/getfilenames/src/test/java/org/apache/hop/pipeline/transforms/getfilenames/GetFileNamesTransformTest.java
 
b/plugins/transforms/getfilenames/src/test/java/org/apache/hop/pipeline/transforms/getfilenames/GetFileNamesTransformTest.java
index 8c08d206de..3b0e8846de 100644
--- 
a/plugins/transforms/getfilenames/src/test/java/org/apache/hop/pipeline/transforms/getfilenames/GetFileNamesTransformTest.java
+++ 
b/plugins/transforms/getfilenames/src/test/java/org/apache/hop/pipeline/transforms/getfilenames/GetFileNamesTransformTest.java
@@ -29,17 +29,27 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import org.apache.hop.core.HopEnvironment;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.fileinput.FileInputList;
+import org.apache.hop.core.fileinput.FileTypeFilter;
 import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.apache.hop.pipeline.transform.RowAdapter;
 import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.io.TempDir;
 
 /** Test class for GetFileNames transform */
 class GetFileNamesTransformTest {
@@ -238,4 +248,156 @@ class GetFileNamesTransformTest {
       fail("Failed to access constants: " + e.getMessage());
     }
   }
+
+  // 
---------------------------------------------------------------------------
+  // Runtime processRow coverage against a real folder tree:
+  //   tempDir/
+  //     file-a.txt  file-b.txt  data.csv
+  //     folder-one/  folder-two/
+  // 
---------------------------------------------------------------------------
+
+  @TempDir private Path tempDir;
+
+  private final List<Object[]> captured = new ArrayList<>();
+  private IRowMeta capturedMeta;
+
+  private static void buildTree(Path root) throws IOException {
+    Files.createDirectories(root.resolve("folder-one"));
+    Files.createDirectories(root.resolve("folder-two"));
+    Files.writeString(root.resolve("file-a.txt"), "alpha");
+    Files.writeString(root.resolve("file-b.txt"), "beta");
+    Files.writeString(root.resolve("data.csv"), "c,d");
+  }
+
+  private GetFileNamesMeta folderMeta(FileTypeFilter filter, String mask) {
+    GetFileNamesMeta meta = new GetFileNamesMeta();
+    meta.getFilterItemList().add(new FilterItem(filter.toString()));
+    meta.getFilesList().add(new FileItem(tempDir.toAbsolutePath().toString(), 
mask, "", "N", "N"));
+    meta.setRaiseAnExceptionIfNoFile(false);
+    meta.setAddResultFile(false);
+    return meta;
+  }
+
+  private GetFileNames runStatic(GetFileNamesMeta meta) throws HopException {
+    captured.clear();
+    capturedMeta = null;
+    GetFileNamesData data = new GetFileNamesData();
+    GetFileNames transform =
+        new GetFileNames(
+            mockHelper.transformMeta, meta, data, 0, mockHelper.pipelineMeta, 
mockHelper.pipeline);
+    transform.addRowListener(
+        new RowAdapter() {
+          @Override
+          public void rowWrittenEvent(IRowMeta rowMeta, Object[] row) {
+            capturedMeta = rowMeta;
+            captured.add(row);
+          }
+        });
+    assertTrue(transform.init(), "init() should succeed");
+    while (transform.processRow()) {
+      // drain every output row
+    }
+    return transform;
+  }
+
+  private Set<String> capturedValues(String field) {
+    int idx = capturedMeta.indexOfValue(field);
+    Set<String> values = new HashSet<>();
+    for (Object[] row : captured) {
+      values.add((String) row[idx]);
+    }
+    return values;
+  }
+
+  @Test
+  void staticFolderAllFilesEmitsRowPerEntry() throws Exception {
+    buildTree(tempDir);
+    runStatic(folderMeta(FileTypeFilter.FILES_AND_FOLDERS, ""));
+
+    assertEquals(5, captured.size());
+    Set<String> types = capturedValues("type");
+    assertTrue(types.contains("file"), "files should be present");
+    assertTrue(types.contains("folder"), "folders should be present");
+    Set<String> names = capturedValues("short_filename");
+    assertTrue(names.contains("file-a.txt"));
+    assertTrue(names.contains("folder-one"));
+  }
+
+  @Test
+  void staticFolderOnlyFoldersEmitsFolderRows() throws Exception {
+    buildTree(tempDir);
+    runStatic(folderMeta(FileTypeFilter.ONLY_FOLDERS, ""));
+
+    assertEquals(2, captured.size(), "only the two sub-folders are expected");
+    for (String type : capturedValues("type")) {
+      assertEquals("folder", type);
+    }
+  }
+
+  @Test
+  void rowLimitStopsOutput() throws Exception {
+    buildTree(tempDir);
+    GetFileNamesMeta meta = folderMeta(FileTypeFilter.FILES_AND_FOLDERS, "");
+    meta.setRowLimit(2);
+    runStatic(meta);
+
+    assertEquals(2, captured.size());
+  }
+
+  @Test
+  void includeRowNumberAddsIncrementingField() throws Exception {
+    buildTree(tempDir);
+    GetFileNamesMeta meta = folderMeta(FileTypeFilter.ONLY_FILES, "");
+    meta.setIncludeRowNumber(true);
+    meta.setRowNumberField("rownr");
+    runStatic(meta);
+
+    assertEquals(3, captured.size());
+    int idx = capturedMeta.indexOfValue("rownr");
+    assertTrue(idx >= 0, "row number field should be present");
+    Set<Long> numbers = new HashSet<>();
+    for (Object[] row : captured) {
+      numbers.add(((Number) row[idx]).longValue());
+    }
+    assertTrue(numbers.contains(1L) && numbers.contains(2L) && 
numbers.contains(3L));
+  }
+
+  @Test
+  void addResultFileRegistersResultFiles() throws Exception {
+    buildTree(tempDir);
+    GetFileNamesMeta meta = folderMeta(FileTypeFilter.ONLY_FILES, "");
+    meta.setAddResultFile(true);
+    GetFileNames transform = runStatic(meta);
+
+    assertEquals(3, captured.size());
+    assertEquals(
+        3, transform.getResultFiles().size(), "each file should be added as a 
result file");
+  }
+
+  @Test
+  void initFailsWhenNoFilesAndRaiseException() {
+    // Empty temp folder, no matching files, raise-an-exception on -> init 
should fail.
+    GetFileNamesMeta meta = folderMeta(FileTypeFilter.ONLY_FILES, "");
+    meta.setRaiseAnExceptionIfNoFile(true);
+    meta.setDoNotFailIfNoFile(false);
+    GetFileNamesData data = new GetFileNamesData();
+    GetFileNames transform =
+        new GetFileNames(
+            mockHelper.transformMeta, meta, data, 0, mockHelper.pipelineMeta, 
mockHelper.pipeline);
+
+    assertFalse(transform.init());
+  }
+
+  @Test
+  void initSucceedsWhenNoFilesButDoNotFail() {
+    GetFileNamesMeta meta = folderMeta(FileTypeFilter.ONLY_FILES, "");
+    meta.setRaiseAnExceptionIfNoFile(true);
+    meta.setDoNotFailIfNoFile(true);
+    GetFileNamesData data = new GetFileNamesData();
+    GetFileNames transform =
+        new GetFileNames(
+            mockHelper.transformMeta, meta, data, 0, mockHelper.pipelineMeta, 
mockHelper.pipeline);
+
+    assertTrue(transform.init());
+  }
 }

Reply via email to