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 cec93c100d Issue #5370 : Pre-fill new data set name, folder, and file
(#8698)
cec93c100d is described below
commit cec93c100d1ee9adaec61dc0896033c6e10ec19a
Author: Matt Casters <[email protected]>
AuthorDate: Thu Oct 1 11:46:07 2026 +0200
Issue #5370 : Pre-fill new data set name, folder, and file (#8698)
* Issue #5370 : Pre-fill new data set name, folder, and file
* Issue #5370 : Leave the data set folder empty when HOP_DATASETS_FOLDER is
not set
---------
Co-authored-by: Bart Maertens <[email protected]>
---
.../ROOT/pages/metadata-types/data-set.adoc | 11 ++
.../main/java/org/apache/hop/testing/DataSet.java | 5 +-
.../org/apache/hop/testing/DataSetDefaults.java | 171 ++++++++++++++++++++
.../apache/hop/testing/gui/TestingGuiPlugin.java | 7 +
.../xp/HopGuiDataSetCreateBeforeDialog.java | 100 ++++++++++++
.../org/apache/hop/ui/testing/DataSetEditor.java | 37 ++++-
.../ui/testing/messages/messages_en_US.properties | 3 +
.../apache/hop/testing/DataSetDefaultsTest.java | 176 +++++++++++++++++++++
.../xp/HopGuiDataSetCreateBeforeDialogTest.java | 60 +++++++
9 files changed, 567 insertions(+), 3 deletions(-)
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/data-set.adoc
b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/data-set.adoc
index 9c8203b59d..5824c261af 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/metadata-types/data-set.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/metadata-types/data-set.adoc
@@ -48,6 +48,17 @@ xref:pipeline/transforms/datasetinput.adoc[Data set input],
xref:pipeline/transf
Describes the file layout for this data set.
|===
+== New data set
+
+Creating a data set suggests values you can still change. An existing data set
is not rewritten.
+
+* *Name* is `ds-<pipeline file>-<transform>`. `<pipeline file>` is the file
name of the pipeline open in the explorer, without its directory or extension.
`<transform>` is included when exactly one transform is selected. Creating a
data set from a transform uses that transform.
+* *Base file name* uses the same text with a `.csv` extension.
+* *Set folder* is `+'${HOP_DATASETS_FOLDER}'+` when that variable is set,
otherwise it stays empty, so no machine-specific path is saved. An empty folder
is resolved when the data set is used.
+* *Browse...* opens in that folder so you can pick it or a subfolder.
+
+If a data set with the suggested name already exists, the suggestion ends with
` 2`, ` 3`, and so on. The base file name follows that name.
+
== Samples
None
\ No newline at end of file
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSet.java
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSet.java
index b3742010c1..1feb07da99 100644
--- a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSet.java
+++ b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSet.java
@@ -49,6 +49,9 @@ public class DataSet extends HopMetadataBase implements
Cloneable, IHopMetadata
public static final String VARIABLE_HOP_DATASETS_FOLDER =
"HOP_DATASETS_FOLDER";
+ /** Placeholder base file name until a suggestion or the user replaces it. */
+ public static final String DEFAULT_BASE_FILENAME = "data-set-filename.csv";
+
@HopMetadataProperty private String description;
@HopMetadataProperty(key = "folder_name")
@@ -62,7 +65,7 @@ public class DataSet extends HopMetadataBase implements
Cloneable, IHopMetadata
public DataSet() {
fields = new ArrayList<>();
- baseFilename = "data-set-filename.csv";
+ baseFilename = DEFAULT_BASE_FILENAME;
}
public DataSet(
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetDefaults.java
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetDefaults.java
new file mode 100644
index 0000000000..cd55f26f5c
--- /dev/null
+++
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/DataSetDefaults.java
@@ -0,0 +1,171 @@
+/*
+ * 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.testing;
+
+import java.util.regex.Pattern;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.metadata.api.IHopMetadataSerializer;
+
+/**
+ * Suggestions for a new data set.
+ *
+ * <p>Name pattern: {@code ds-<pipeline file>-<transform>}. The pipeline part
is the file name
+ * without its directory or extension, and the transform part is omitted when
unknown. The folder is
+ * the datasets-folder variable expression when that variable is set.
Otherwise it stays empty, so
+ * no machine-specific path is stored and the folder is resolved when the data
set is used. A value
+ * that is already set is left alone, except the constructor placeholder base
file name, which is
+ * replaced when a name is known. A second call does not replace suggestions.
+ */
+public final class DataSetDefaults {
+
+ private static final Pattern UNSAFE =
Pattern.compile("[\\\\/:*?\"<>|\\p{Cntrl}]+");
+ private static final Pattern REPEATED_DASH = Pattern.compile("-{2,}");
+ private static final Pattern EDGE_DASH =
Pattern.compile("^[\\s-]+|[\\s-]+$");
+
+ private DataSetDefaults() {}
+
+ public static void apply(
+ DataSet dataSet,
+ String pipelineFilename,
+ String transformName,
+ IVariables variables,
+ IHopMetadataProvider metadataProvider) {
+ if (dataSet == null) {
+ return;
+ }
+
+ if (StringUtils.isEmpty(dataSet.getName())) {
+ String generated = buildName(pipelineFilename, transformName);
+ if (generated != null) {
+ dataSet.setName(uniqueName(generated, metadataProvider));
+ }
+ }
+
+ if (StringUtils.isEmpty(dataSet.getFolderName())) {
+ String folder = defaultFolder(variables);
+ if (StringUtils.isNotEmpty(folder)) {
+ dataSet.setFolderName(folder);
+ }
+ }
+
+ if (isPlaceholderFilename(dataSet) &&
StringUtils.isNotEmpty(dataSet.getName())) {
+ dataSet.setBaseFilename(dataSet.getName() + ".csv");
+ }
+ }
+
+ static String buildName(String pipelineFilename, String transformName) {
+ String pipeline = sanitizeToken(pipelineStem(pipelineFilename));
+ String transform = sanitizeToken(transformName);
+ if (pipeline == null && transform == null) {
+ return null;
+ }
+ StringBuilder name = new StringBuilder("ds");
+ if (pipeline != null) {
+ name.append('-').append(pipeline);
+ }
+ if (transform != null) {
+ name.append('-').append(transform);
+ }
+ return name.toString();
+ }
+
+ static String pipelineStem(String pipelineFilename) {
+ String file = filenameOnly(pipelineFilename);
+ if (file == null) {
+ return null;
+ }
+ String stem = FilenameUtils.getBaseName(file);
+ return StringUtils.isBlank(stem) ? null : stem;
+ }
+
+ static String defaultFolder(IVariables variables) {
+ return datasetsFolderExpression(variables);
+ }
+
+ /**
+ * The datasets-folder variable expression when that variable is set on
{@code variables} or a
+ * parent, otherwise null.
+ */
+ public static String datasetsFolderExpression(IVariables variables) {
+ if (!hasDatasetsFolder(variables)) {
+ return null;
+ }
+ return "${" + DataSet.VARIABLE_HOP_DATASETS_FOLDER + "}";
+ }
+
+ private static boolean isPlaceholderFilename(DataSet dataSet) {
+ return StringUtils.isEmpty(dataSet.getBaseFilename())
+ || DataSet.DEFAULT_BASE_FILENAME.equals(dataSet.getBaseFilename());
+ }
+
+ private static String filenameOnly(String pipelineFilename) {
+ if (StringUtils.isBlank(pipelineFilename)) {
+ return null;
+ }
+ String normalized = pipelineFilename.trim().replace('\\', '/');
+ int slash = normalized.lastIndexOf('/');
+ String file = slash >= 0 ? normalized.substring(slash + 1) : normalized;
+ return StringUtils.isBlank(file) ? null : file;
+ }
+
+ static String sanitizeToken(String value) {
+ if (value == null) {
+ return null;
+ }
+ String cleaned = UNSAFE.matcher(value.trim()).replaceAll("-");
+ cleaned = REPEATED_DASH.matcher(cleaned).replaceAll("-");
+ cleaned = EDGE_DASH.matcher(cleaned).replaceAll("");
+ return cleaned.isEmpty() ? null : cleaned;
+ }
+
+ private static boolean hasDatasetsFolder(IVariables variables) {
+ IVariables current = variables;
+ while (current != null) {
+ String value = current.getVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER);
+ if (StringUtils.isNotBlank(value)) {
+ return true;
+ }
+ current = current.getParentVariables();
+ }
+ return false;
+ }
+
+ private static String uniqueName(String base, IHopMetadataProvider
metadataProvider) {
+ if (metadataProvider == null) {
+ return base;
+ }
+ try {
+ IHopMetadataSerializer<DataSet> serializer =
metadataProvider.getSerializer(DataSet.class);
+ if (!serializer.exists(base)) {
+ return base;
+ }
+ int suffix = 2;
+ String candidate = base + " " + suffix;
+ while (serializer.exists(candidate) && suffix < 10000) {
+ suffix++;
+ candidate = base + " " + suffix;
+ }
+ return candidate;
+ } catch (Exception e) {
+ return base;
+ }
+ }
+}
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
index 2ee7296b6a..e2d8df47e0 100644
---
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
+++
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
@@ -55,6 +55,7 @@ import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.testing.DataSet;
import org.apache.hop.testing.DataSetCsvUtil;
+import org.apache.hop.testing.DataSetDefaults;
import org.apache.hop.testing.DataSetField;
import org.apache.hop.testing.PipelineTweak;
import org.apache.hop.testing.PipelineUnitTest;
@@ -747,6 +748,12 @@ public class TestingGuiPlugin {
try {
DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(
+ dataSet,
+ pipelineMeta.getFilename(),
+ transformMeta.getName(),
+ variables,
+ metadataProvider);
IRowMeta rowMeta = pipelineMeta.getTransformFields(variables,
transformMeta);
for (int i = 0; i < rowMeta.size(); i++) {
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialog.java
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialog.java
new file mode 100644
index 0000000000..52b38c72b9
--- /dev/null
+++
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialog.java
@@ -0,0 +1,100 @@
+/*
+ * 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.testing.xp;
+
+import java.util.List;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.extension.ExtensionPoint;
+import org.apache.hop.core.extension.IExtensionPoint;
+import org.apache.hop.core.logging.ILogChannel;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.apache.hop.testing.DataSet;
+import org.apache.hop.testing.DataSetDefaults;
+import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.file.pipeline.HopGuiPipelineGraph;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
+
+/**
+ * Suggests a name, folder and base file name before the new data set dialog
opens. The pipeline is
+ * the one open in the explorer, including when the metadata perspective is
active. A transform name
+ * is included only when exactly one transform is selected; the
create-from-transform action sets
+ * the clicked transform itself before this runs.
+ */
+@ExtensionPoint(
+ id = "HopGuiDataSetCreateBeforeDialog",
+ extensionPointId = "HopGuiMetadataObjectCreateBeforeDialog",
+ description = "Suggests a name, folder and filename for a new data set")
+public class HopGuiDataSetCreateBeforeDialog implements
IExtensionPoint<Object> {
+
+ @Override
+ public void callExtensionPoint(ILogChannel log, IVariables variables, Object
object)
+ throws HopException {
+ if (!(object instanceof DataSet dataSet)) {
+ return;
+ }
+ try {
+ HopGui hopGui = HopGui.peekInstance();
+ PipelineMeta pipelineMeta = activeExplorerPipeline(hopGui);
+ IHopMetadataProvider metadataProvider = hopGui == null ? null :
hopGui.getMetadataProvider();
+ DataSetDefaults.apply(
+ dataSet,
+ pipelineMeta == null ? null : pipelineMeta.getFilename(),
+ singleSelectedTransformName(pipelineMeta),
+ variables,
+ metadataProvider);
+ } catch (Exception e) {
+ log.logError("Error suggesting defaults for a new data set", e);
+ }
+ }
+
+ /**
+ * @return the transform name when exactly one transform is selected,
otherwise null so several
+ * selected transforms are not collapsed into an arbitrary choice
+ */
+ static String singleSelectedTransformName(PipelineMeta pipelineMeta) {
+ if (pipelineMeta == null) {
+ return null;
+ }
+ List<TransformMeta> selected = pipelineMeta.getSelectedTransforms();
+ if (selected == null || selected.size() != 1) {
+ return null;
+ }
+ TransformMeta transform = selected.get(0);
+ return transform == null ? null : transform.getName();
+ }
+
+ private static PipelineMeta activeExplorerPipeline(HopGui hopGui) {
+ if (hopGui == null || hopGui.getPerspectiveManager() == null) {
+ return null;
+ }
+ ExplorerPerspective explorer =
+
hopGui.getPerspectiveManager().findPerspective(ExplorerPerspective.class);
+ if (explorer == null) {
+ return null;
+ }
+ IHopFileTypeHandler handler = explorer.getActiveFileTypeHandler();
+ if (handler instanceof HopGuiPipelineGraph graph) {
+ return graph.getPipelineMeta();
+ }
+ return null;
+ }
+}
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/DataSetEditor.java
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/DataSetEditor.java
index f2350a4b3c..f082cb1122 100644
---
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/DataSetEditor.java
+++
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/DataSetEditor.java
@@ -30,8 +30,10 @@ import org.apache.hop.core.vfs.HopVfs;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.testing.DataSet;
import org.apache.hop.testing.DataSetCsvUtil;
+import org.apache.hop.testing.DataSetDefaults;
import org.apache.hop.testing.DataSetField;
import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
import org.apache.hop.ui.core.dialog.ShowRowsDialog;
@@ -49,6 +51,7 @@ import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
@@ -138,12 +141,23 @@ public class DataSetEditor extends
MetadataEditor<DataSet> {
fdlFolderName.left = new FormAttachment(0, 0);
fdlFolderName.right = new FormAttachment(100, 0);
wlFolderName.setLayoutData(fdlFolderName);
+
+ Button wBrowseFolder = new Button(parent, SWT.PUSH);
+ PropsUi.setLook(wBrowseFolder);
+ wBrowseFolder.setText(BaseMessages.getString(PKG,
"DataSetDialog.Browse.Button"));
+ wBrowseFolder.setToolTipText(BaseMessages.getString(PKG,
"DataSetDialog.Browse.Tooltip"));
+ FormData fdbFolderName = new FormData();
+ fdbFolderName.right = new FormAttachment(100, 0);
+ fdbFolderName.top = new FormAttachment(wlFolderName, margin);
+ wBrowseFolder.setLayoutData(fdbFolderName);
+ wBrowseFolder.addListener(SWT.Selection, e ->
browseForFolder(parent.getShell()));
+
wFolderName = new TextVar(manager.getVariables(), parent, SWT.SINGLE |
SWT.LEFT | SWT.BORDER);
PropsUi.setLook(wFolderName);
FormData fdFolderName = new FormData();
fdFolderName.top = new FormAttachment(wlFolderName, margin);
fdFolderName.left = new FormAttachment(0, 0);
- fdFolderName.right = new FormAttachment(100, 0);
+ fdFolderName.right = new FormAttachment(wBrowseFolder, -margin);
wFolderName.setLayoutData(fdFolderName);
// The table storing the set...
@@ -152,7 +166,7 @@ public class DataSetEditor extends MetadataEditor<DataSet> {
PropsUi.setLook(wlBaseFilename);
wlBaseFilename.setText(BaseMessages.getString(PKG,
"DataSetDialog.BaseFilename.Label"));
FormData fdlBaseFilename = new FormData();
- fdlBaseFilename.top = new FormAttachment(wFolderName, margin);
+ fdlBaseFilename.top = new FormAttachment(wBrowseFolder, margin);
fdlBaseFilename.left = new FormAttachment(0, 0);
fdlBaseFilename.right = new FormAttachment(100, 0);
wlBaseFilename.setLayoutData(fdlBaseFilename);
@@ -376,6 +390,25 @@ public class DataSetEditor extends MetadataEditor<DataSet>
{
super.save();
}
+ private void browseForFolder(Shell shell) {
+ String path = wFolderName.getText();
+ if (StringUtil.isEmpty(path)) {
+ String configured =
DataSetDefaults.datasetsFolderExpression(manager.getVariables());
+ if (configured != null) {
+ path = configured;
+ }
+ }
+ String directory =
+ BaseDialog.presentDirectoryDialog(
+ shell,
+ path,
+ BaseMessages.getString(PKG, "DataSetDialog.Browse.Message"),
+ manager.getVariables());
+ if (directory != null) {
+ wFolderName.setText(directory);
+ }
+ }
+
private void verifySettings() throws HopException {
try {
if (StringUtil.isEmpty(wBaseFilename.getText())) {
diff --git
a/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
b/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
index 16b09c895f..54b3cfd8fd 100644
---
a/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
+++
b/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
@@ -18,6 +18,9 @@
#
DataSetDialog.BaseFilename.Label=Base file name
+DataSetDialog.Browse.Button=Browse...
+DataSetDialog.Browse.Message=Select the data set folder
+DataSetDialog.Browse.Tooltip=Open the folder above so you can choose it or a
subfolder. When that folder is empty this starts in '${HOP_DATASETS_FOLDER}' if
that variable is set.
DataSetDialog.ColumnInfo.Comment=Comment
DataSetDialog.ColumnInfo.FieldFormat=Format
DataSetDialog.ColumnInfo.FieldLength=Length
diff --git
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetDefaultsTest.java
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetDefaultsTest.java
new file mode 100644
index 0000000000..f7973ee4ab
--- /dev/null
+++
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/DataSetDefaultsTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.testing;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
+import org.junit.jupiter.api.Test;
+
+class DataSetDefaultsTest {
+
+ @Test
+ void suggestsNameFolderAndFilenameFromPipelineAndTransform() throws
Exception {
+ Variables variables = new Variables();
+ variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER,
"${PROJECT_HOME}/datasets");
+
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(
+ dataSet, "${PROJECT_HOME}/pipelines/load-orders.hpl", "Table input",
variables, null);
+
+ assertEquals("ds-load-orders-Table input", dataSet.getName());
+ assertEquals("${HOP_DATASETS_FOLDER}", dataSet.getFolderName());
+ assertEquals("ds-load-orders-Table input.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void leavesFolderEmptyWhenDatasetsFolderIsNotConfigured() {
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, "C:\\proj\\pipe.hpl", "Check", new
Variables(), null);
+
+ assertEquals("ds-pipe-Check", dataSet.getName());
+ assertNull(dataSet.getFolderName());
+ assertEquals("ds-pipe-Check.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void readsDatasetsFolderFromParentVariableSpace() {
+ Variables parent = new Variables();
+ parent.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, "/data/sets");
+ Variables child = new Variables();
+ child.setParentVariables(parent);
+
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, "/tmp/pipe/load.hpl", "Check", child, null);
+
+ assertEquals("${HOP_DATASETS_FOLDER}", dataSet.getFolderName());
+ }
+
+ @Test
+ void blankDatasetsFolderLeavesFolderEmpty() {
+ Variables variables = new Variables();
+ variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, " ");
+
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, "/tmp/pipe/load.hpl", null, variables,
null);
+
+ assertEquals("ds-load", dataSet.getName());
+ assertNull(dataSet.getFolderName());
+ assertEquals("ds-load.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void sanitizesPathCharactersInTheTransformName() {
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, "dir/a:b.hpl", "in/out", new Variables(),
null);
+
+ assertEquals("ds-a-b-in-out", dataSet.getName());
+ assertNull(dataSet.getFolderName());
+ assertEquals("ds-a-b-in-out.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void suggestsFromTransformOnlyWhenThePipelineHasNoFilename() {
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, null, "Check", new Variables(), null);
+
+ assertEquals("ds-Check", dataSet.getName());
+ assertNull(dataSet.getFolderName());
+ assertEquals("ds-Check.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void leavesNameEmptyWhenPipelineAndTransformAreUnknown() {
+ Variables variables = new Variables();
+ variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, "/data/sets");
+
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, " ", " ", variables, null);
+
+ assertNull(dataSet.getName());
+ assertEquals("${HOP_DATASETS_FOLDER}", dataSet.getFolderName());
+ assertEquals(DataSet.DEFAULT_BASE_FILENAME, dataSet.getBaseFilename());
+ }
+
+ @Test
+ void doesNotReplaceValuesTheUserAlreadySet() {
+ Variables variables = new Variables();
+ variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, "/data/sets");
+
+ DataSet dataSet = new DataSet();
+ dataSet.setName("kept");
+ dataSet.setFolderName("kept-folder");
+ dataSet.setBaseFilename("kept.csv");
+ DataSetDefaults.apply(dataSet, "/tmp/pipe.hpl", "Step", variables, null);
+
+ assertEquals("kept", dataSet.getName());
+ assertEquals("kept-folder", dataSet.getFolderName());
+ assertEquals("kept.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void replacesPlaceholderFilenameWhenTheNameIsAlreadySet() {
+ DataSet dataSet = new DataSet();
+ dataSet.setName("golden");
+ dataSet.setFolderName("folder");
+
+ DataSetDefaults.apply(dataSet, "/tmp/other.hpl", "Other", new Variables(),
null);
+
+ assertEquals("golden", dataSet.getName());
+ assertEquals("folder", dataSet.getFolderName());
+ assertEquals("golden.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void secondApplyDoesNotReplaceTheSuggestion() {
+ Variables variables = new Variables();
+ variables.setVariable(DataSet.VARIABLE_HOP_DATASETS_FOLDER, "/data/sets");
+ DataSet dataSet = new DataSet();
+
+ DataSetDefaults.apply(dataSet, "/tmp/load.hpl", "Check", variables, null);
+ DataSetDefaults.apply(dataSet, "/tmp/other.hpl", "Other", variables, null);
+
+ assertEquals("ds-load-Check", dataSet.getName());
+ assertEquals("${HOP_DATASETS_FOLDER}", dataSet.getFolderName());
+ assertEquals("ds-load-Check.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void avoidsAnExistingDataSetName() throws Exception {
+ MemoryMetadataProvider provider = new MemoryMetadataProvider();
+ DataSet existing = new DataSet();
+ existing.setName("ds-load-orders-Check");
+ provider.getSerializer(DataSet.class).save(existing);
+ DataSet second = new DataSet();
+ second.setName("ds-load-orders-Check 2");
+ provider.getSerializer(DataSet.class).save(second);
+
+ DataSet dataSet = new DataSet();
+ DataSetDefaults.apply(dataSet, "/p/load-orders.hpl", "Check", new
Variables(), provider);
+
+ assertEquals("ds-load-orders-Check 3", dataSet.getName());
+ assertEquals("ds-load-orders-Check 3.csv", dataSet.getBaseFilename());
+ }
+
+ @Test
+ void nullDataSetIsIgnored() {
+ DataSetDefaults.apply(null, "/tmp/pipe.hpl", "Check", new Variables(),
null);
+ }
+}
diff --git
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialogTest.java
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialogTest.java
new file mode 100644
index 0000000000..35e51930e8
--- /dev/null
+++
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/xp/HopGuiDataSetCreateBeforeDialogTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.testing.xp;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.junit.jupiter.api.Test;
+
+class HopGuiDataSetCreateBeforeDialogTest {
+
+ @Test
+ void singleSelectedTransformNameUsesTheOnlySelection() {
+ PipelineMeta pipelineMeta = new PipelineMeta();
+ pipelineMeta.addTransform(transform("Ignored", false));
+ pipelineMeta.addTransform(transform("Check", true));
+
+ assertEquals(
+ "Check",
HopGuiDataSetCreateBeforeDialog.singleSelectedTransformName(pipelineMeta));
+ }
+
+ @Test
+ void singleSelectedTransformNameIgnoresZeroOrSeveralSelections() {
+
assertNull(HopGuiDataSetCreateBeforeDialog.singleSelectedTransformName(null));
+
+ PipelineMeta none = new PipelineMeta();
+ none.addTransform(transform("Check", false));
+
assertNull(HopGuiDataSetCreateBeforeDialog.singleSelectedTransformName(none));
+
+ PipelineMeta several = new PipelineMeta();
+ several.addTransform(transform("One", true));
+ several.addTransform(transform("Two", true));
+
assertNull(HopGuiDataSetCreateBeforeDialog.singleSelectedTransformName(several));
+ }
+
+ private static TransformMeta transform(String name, boolean selected) {
+ TransformMeta transformMeta = new TransformMeta(name, null);
+ if (selected) {
+ transformMeta.flipSelected();
+ }
+ return transformMeta;
+ }
+}