manager, NamingScheme metadata) {
+ super(hopGui, manager, metadata);
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ PropsUi props = PropsUi.getInstance();
+ int middle = props.getMiddlePct();
+ int margin = PropsUi.getMargin() + 2;
+
+ // Name
+ Label wlName = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlName);
+ wlName.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.Name.Label"));
+ FormData fdlName = new FormData();
+ fdlName.top = new FormAttachment(0, margin);
+ fdlName.left = new FormAttachment(0, 0);
+ fdlName.right = new FormAttachment(middle, -margin);
+ wlName.setLayoutData(fdlName);
+ wName = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wName);
+ FormData fdName = new FormData();
+ fdName.top = new FormAttachment(wlName, 0, SWT.CENTER);
+ fdName.left = new FormAttachment(middle, 0);
+ fdName.right = new FormAttachment(100, 0);
+ wName.setLayoutData(fdName);
+ Control lastControl = wName;
+
+ // Description
+ Label wlDescription = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlDescription);
+ wlDescription.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.Description.Label"));
+ FormData fdlDescription = new FormData();
+ fdlDescription.top = new FormAttachment(lastControl, margin);
+ fdlDescription.left = new FormAttachment(0, 0);
+ fdlDescription.right = new FormAttachment(middle, -margin);
+ wlDescription.setLayoutData(fdlDescription);
+ wDescription = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wDescription);
+ FormData fdDescription = new FormData();
+ fdDescription.top = new FormAttachment(wlDescription, 0, SWT.CENTER);
+ fdDescription.left = new FormAttachment(middle, 0);
+ fdDescription.right = new FormAttachment(100, 0);
+ wDescription.setLayoutData(fdDescription);
+ lastControl = wDescription;
+
+ // Type
+ Label wlType = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlType);
+ wlType.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.Type.Label"));
+ FormData fdlType = new FormData();
+ fdlType.top = new FormAttachment(lastControl, margin);
+ fdlType.left = new FormAttachment(0, 0);
+ fdlType.right = new FormAttachment(middle, -margin);
+ wlType.setLayoutData(fdlType);
+ wType = new Combo(parent, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER | SWT.LEFT);
+ PropsUi.setLook(wType);
+ wType.setItems(NamingSchemeType.getLabels());
+ FormData fdType = new FormData();
+ fdType.top = new FormAttachment(wlType, 0, SWT.CENTER);
+ fdType.left = new FormAttachment(middle, 0);
+ fdType.right = new FormAttachment(100, 0);
+ wType.setLayoutData(fdType);
+ lastControl = wType;
+
+ // Case style
+ Label wlCaseStyle = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlCaseStyle);
+ wlCaseStyle.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.CaseStyle.Label"));
+ FormData fdlCaseStyle = new FormData();
+ fdlCaseStyle.top = new FormAttachment(lastControl, margin);
+ fdlCaseStyle.left = new FormAttachment(0, 0);
+ fdlCaseStyle.right = new FormAttachment(middle, -margin);
+ wlCaseStyle.setLayoutData(fdlCaseStyle);
+ wCaseStyle = new Combo(parent, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER | SWT.LEFT);
+ PropsUi.setLook(wCaseStyle);
+ wCaseStyle.setItems(NamingCaseStyle.getLabels());
+ FormData fdCaseStyle = new FormData();
+ fdCaseStyle.top = new FormAttachment(wlCaseStyle, 0, SWT.CENTER);
+ fdCaseStyle.left = new FormAttachment(middle, 0);
+ fdCaseStyle.right = new FormAttachment(100, 0);
+ wCaseStyle.setLayoutData(fdCaseStyle);
+ lastControl = wCaseStyle;
+
+ // Word separator
+ Label wlWordSeparator = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlWordSeparator);
+ wlWordSeparator.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.WordSeparator.Label"));
+ FormData fdlWordSeparator = new FormData();
+ fdlWordSeparator.top = new FormAttachment(lastControl, margin);
+ fdlWordSeparator.left = new FormAttachment(0, 0);
+ fdlWordSeparator.right = new FormAttachment(middle, -margin);
+ wlWordSeparator.setLayoutData(fdlWordSeparator);
+ wWordSeparator = new Combo(parent, SWT.SINGLE | SWT.READ_ONLY | SWT.BORDER | SWT.LEFT);
+ PropsUi.setLook(wWordSeparator);
+ wWordSeparator.setItems(NamingWordSeparator.getLabels());
+ FormData fdWordSeparator = new FormData();
+ fdWordSeparator.top = new FormAttachment(wlWordSeparator, 0, SWT.CENTER);
+ fdWordSeparator.left = new FormAttachment(middle, 0);
+ fdWordSeparator.right = new FormAttachment(100, 0);
+ wWordSeparator.setLayoutData(fdWordSeparator);
+ lastControl = wWordSeparator;
+
+ // Extra delimiters
+ Label wlExtra = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlExtra);
+ wlExtra.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.ExtraDelimiters.Label"));
+ FormData fdlExtra = new FormData();
+ fdlExtra.top = new FormAttachment(lastControl, margin);
+ fdlExtra.left = new FormAttachment(0, 0);
+ fdlExtra.right = new FormAttachment(middle, -margin);
+ wlExtra.setLayoutData(fdlExtra);
+ wExtraDelimiters = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wExtraDelimiters);
+ FormData fdExtra = new FormData();
+ fdExtra.top = new FormAttachment(wlExtra, 0, SWT.CENTER);
+ fdExtra.left = new FormAttachment(middle, 0);
+ fdExtra.right = new FormAttachment(100, 0);
+ wExtraDelimiters.setLayoutData(fdExtra);
+ lastControl = wExtraDelimiters;
+
+ // Remove special characters
+ Label wlRemoveSpecial = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlRemoveSpecial);
+ wlRemoveSpecial.setText(
+ BaseMessages.getString(PKG, "NamingSchemeEditor.RemoveSpecialCharacters.Label"));
+ FormData fdlRemoveSpecial = new FormData();
+ fdlRemoveSpecial.top = new FormAttachment(lastControl, margin);
+ fdlRemoveSpecial.left = new FormAttachment(0, 0);
+ fdlRemoveSpecial.right = new FormAttachment(middle, -margin);
+ wlRemoveSpecial.setLayoutData(fdlRemoveSpecial);
+ wRemoveSpecial = new Button(parent, SWT.CHECK | SWT.LEFT);
+ PropsUi.setLook(wRemoveSpecial);
+ FormData fdRemoveSpecial = new FormData();
+ fdRemoveSpecial.top = new FormAttachment(wlRemoveSpecial, 0, SWT.CENTER);
+ fdRemoveSpecial.left = new FormAttachment(middle, 0);
+ fdRemoveSpecial.right = new FormAttachment(100, 0);
+ wRemoveSpecial.setLayoutData(fdRemoveSpecial);
+ lastControl = wlRemoveSpecial;
+
+ // Collapse separators
+ Label wlCollapse = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlCollapse);
+ wlCollapse.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.CollapseSeparators.Label"));
+ FormData fdlCollapse = new FormData();
+ fdlCollapse.top = new FormAttachment(lastControl, margin);
+ fdlCollapse.left = new FormAttachment(0, 0);
+ fdlCollapse.right = new FormAttachment(middle, -margin);
+ wlCollapse.setLayoutData(fdlCollapse);
+ wCollapseSeparators = new Button(parent, SWT.CHECK | SWT.LEFT);
+ PropsUi.setLook(wCollapseSeparators);
+ FormData fdCollapse = new FormData();
+ fdCollapse.top = new FormAttachment(wlCollapse, 0, SWT.CENTER);
+ fdCollapse.left = new FormAttachment(middle, 0);
+ fdCollapse.right = new FormAttachment(100, 0);
+ wCollapseSeparators.setLayoutData(fdCollapse);
+ lastControl = wlCollapse;
+
+ // Trim edge separators
+ Label wlTrim = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlTrim);
+ wlTrim.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.TrimEdgeSeparators.Label"));
+ FormData fdlTrim = new FormData();
+ fdlTrim.top = new FormAttachment(lastControl, margin);
+ fdlTrim.left = new FormAttachment(0, 0);
+ fdlTrim.right = new FormAttachment(middle, -margin);
+ wlTrim.setLayoutData(fdlTrim);
+ wTrimEdges = new Button(parent, SWT.CHECK | SWT.LEFT);
+ PropsUi.setLook(wTrimEdges);
+ FormData fdTrim = new FormData();
+ fdTrim.top = new FormAttachment(wlTrim, 0, SWT.CENTER);
+ fdTrim.left = new FormAttachment(middle, 0);
+ fdTrim.right = new FormAttachment(100, 0);
+ wTrimEdges.setLayoutData(fdTrim);
+ lastControl = wlTrim;
+
+ // Prefix
+ Label wlPrefix = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlPrefix);
+ wlPrefix.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.Prefix.Label"));
+ FormData fdlPrefix = new FormData();
+ fdlPrefix.top = new FormAttachment(lastControl, margin);
+ fdlPrefix.left = new FormAttachment(0, 0);
+ fdlPrefix.right = new FormAttachment(middle, -margin);
+ wlPrefix.setLayoutData(fdlPrefix);
+ wPrefix = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wPrefix);
+ FormData fdPrefix = new FormData();
+ fdPrefix.top = new FormAttachment(wlPrefix, 0, SWT.CENTER);
+ fdPrefix.left = new FormAttachment(middle, 0);
+ fdPrefix.right = new FormAttachment(100, 0);
+ wPrefix.setLayoutData(fdPrefix);
+ lastControl = wPrefix;
+
+ // Suffix
+ Label wlSuffix = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlSuffix);
+ wlSuffix.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.Suffix.Label"));
+ FormData fdlSuffix = new FormData();
+ fdlSuffix.top = new FormAttachment(lastControl, margin);
+ fdlSuffix.left = new FormAttachment(0, 0);
+ fdlSuffix.right = new FormAttachment(middle, -margin);
+ wlSuffix.setLayoutData(fdlSuffix);
+ wSuffix = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wSuffix);
+ FormData fdSuffix = new FormData();
+ fdSuffix.top = new FormAttachment(wlSuffix, 0, SWT.CENTER);
+ fdSuffix.left = new FormAttachment(middle, 0);
+ fdSuffix.right = new FormAttachment(100, 0);
+ wSuffix.setLayoutData(fdSuffix);
+ lastControl = wSuffix;
+
+ // Preview input
+ Label wlPreviewInput = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlPreviewInput);
+ wlPreviewInput.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.PreviewInput.Label"));
+ FormData fdlPreviewInput = new FormData();
+ fdlPreviewInput.top = new FormAttachment(lastControl, margin * 2);
+ fdlPreviewInput.left = new FormAttachment(0, 0);
+ fdlPreviewInput.right = new FormAttachment(middle, -margin);
+ wlPreviewInput.setLayoutData(fdlPreviewInput);
+ wPreviewInput = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ PropsUi.setLook(wPreviewInput);
+ wPreviewInput.setText("Order ID");
+ FormData fdPreviewInput = new FormData();
+ fdPreviewInput.top = new FormAttachment(wlPreviewInput, 0, SWT.CENTER);
+ fdPreviewInput.left = new FormAttachment(middle, 0);
+ fdPreviewInput.right = new FormAttachment(100, 0);
+ wPreviewInput.setLayoutData(fdPreviewInput);
+ lastControl = wPreviewInput;
+
+ // Preview output (read-only)
+ Label wlPreviewOutput = new Label(parent, SWT.RIGHT);
+ PropsUi.setLook(wlPreviewOutput);
+ wlPreviewOutput.setText(BaseMessages.getString(PKG, "NamingSchemeEditor.PreviewOutput.Label"));
+ FormData fdlPreviewOutput = new FormData();
+ fdlPreviewOutput.top = new FormAttachment(lastControl, margin);
+ fdlPreviewOutput.left = new FormAttachment(0, 0);
+ fdlPreviewOutput.right = new FormAttachment(middle, -margin);
+ wlPreviewOutput.setLayoutData(fdlPreviewOutput);
+ wPreviewOutput = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER | SWT.READ_ONLY);
+ PropsUi.setLook(wPreviewOutput);
+ FormData fdPreviewOutput = new FormData();
+ fdPreviewOutput.top = new FormAttachment(wlPreviewOutput, 0, SWT.CENTER);
+ fdPreviewOutput.left = new FormAttachment(middle, 0);
+ fdPreviewOutput.right = new FormAttachment(100, 0);
+ wPreviewOutput.setLayoutData(fdPreviewOutput);
+
+ setWidgetsContent();
+ updatePreview();
+
+ wName.addModifyListener(e -> setChanged());
+ wDescription.addModifyListener(e -> setChanged());
+ wType.addModifyListener(e -> setChanged());
+ wCaseStyle.addModifyListener(
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wWordSeparator.addModifyListener(
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wExtraDelimiters.addModifyListener(
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wRemoveSpecial.addListener(
+ SWT.Selection,
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wCollapseSeparators.addListener(
+ SWT.Selection,
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wTrimEdges.addListener(
+ SWT.Selection,
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wPrefix.addModifyListener(
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wSuffix.addModifyListener(
+ e -> {
+ setChanged();
+ updatePreview();
+ });
+ wPreviewInput.addModifyListener(e -> updatePreview());
+ }
+
+ private void updatePreview() {
+ if (wPreviewOutput == null || wPreviewOutput.isDisposed()) {
+ return;
+ }
+ NamingScheme preview = new NamingScheme();
+ fillSchemeFromWidgets(preview);
+ String input = wPreviewInput != null ? wPreviewInput.getText() : "";
+ wPreviewOutput.setText(Const.NVL(NamingEngine.apply(preview, input), ""));
+ }
+
+ private void fillSchemeFromWidgets(NamingScheme scheme) {
+ scheme.setName(wName.getText());
+ scheme.setDescription(wDescription.getText());
+ scheme.setType(NamingSchemeType.fromLabel(wType.getText()).getCode());
+ scheme.setCaseStyle(NamingCaseStyle.fromLabel(wCaseStyle.getText()).getCode());
+ scheme.setWordSeparator(NamingWordSeparator.fromLabel(wWordSeparator.getText()).getCode());
+ scheme.setExtraDelimiters(wExtraDelimiters.getText());
+ scheme.setRemoveSpecialCharacters(wRemoveSpecial.getSelection());
+ scheme.setCollapseRepeatedSeparators(wCollapseSeparators.getSelection());
+ scheme.setTrimEdgeSeparators(wTrimEdges.getSelection());
+ scheme.setPrefix(wPrefix.getText());
+ scheme.setSuffix(wSuffix.getText());
+ }
+
+ @Override
+ public void setWidgetsContent() {
+ NamingScheme scheme = getMetadata();
+ wName.setText(Const.NVL(scheme.getName(), ""));
+ wDescription.setText(Const.NVL(scheme.getDescription(), ""));
+ wType.setText(NamingSchemeType.fromCode(scheme.getType()).getLabel());
+ wCaseStyle.setText(NamingCaseStyle.fromCode(scheme.getCaseStyle()).getLabel());
+ wWordSeparator.setText(NamingWordSeparator.fromCode(scheme.getWordSeparator()).getLabel());
+ wExtraDelimiters.setText(Const.NVL(scheme.getExtraDelimiters(), ""));
+ wRemoveSpecial.setSelection(scheme.isRemoveSpecialCharacters());
+ wCollapseSeparators.setSelection(scheme.isCollapseRepeatedSeparators());
+ wTrimEdges.setSelection(scheme.isTrimEdgeSeparators());
+ wPrefix.setText(Const.NVL(scheme.getPrefix(), ""));
+ wSuffix.setText(Const.NVL(scheme.getSuffix(), ""));
+ }
+
+ @Override
+ public void getWidgetsContent(NamingScheme scheme) {
+ fillSchemeFromWidgets(scheme);
+ }
+
+ @Override
+ public boolean setFocus() {
+ if (wName == null || wName.isDisposed()) {
+ return false;
+ }
+ return wName.setFocus();
+ }
+}
diff --git a/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingSchemeType.java b/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingSchemeType.java
new file mode 100644
index 00000000000..c16bdb6193a
--- /dev/null
+++ b/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingSchemeType.java
@@ -0,0 +1,92 @@
+/*
+ * 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.naming.metadata;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hop.i18n.BaseMessages;
+
+/**
+ * Target kind for a {@link NamingScheme}. Stored as a stable string code on the metadata element.
+ */
+public enum NamingSchemeType {
+ HOP_FIELD("hop-field", "NamingSchemeType.HopField"),
+ DATABASE_TABLE("database-table", "NamingSchemeType.DatabaseTable"),
+ DATABASE_COLUMN("database-column", "NamingSchemeType.DatabaseColumn"),
+ FILE("file", "NamingSchemeType.File"),
+ FOLDER("folder", "NamingSchemeType.Folder");
+
+ private static final Class> PKG = NamingScheme.class;
+
+ private final String code;
+ private final String i18nKey;
+
+ NamingSchemeType(String code, String i18nKey) {
+ this.code = code;
+ this.i18nKey = i18nKey;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getLabel() {
+ return BaseMessages.getString(PKG, i18nKey);
+ }
+
+ public static NamingSchemeType fromCode(String code) {
+ if (StringUtils.isEmpty(code)) {
+ return HOP_FIELD;
+ }
+ for (NamingSchemeType type : values()) {
+ if (type.code.equalsIgnoreCase(code.trim())) {
+ return type;
+ }
+ }
+ return HOP_FIELD;
+ }
+
+ public static String[] getCodes() {
+ NamingSchemeType[] values = values();
+ String[] codes = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ codes[i] = values[i].code;
+ }
+ return codes;
+ }
+
+ public static String[] getLabels() {
+ NamingSchemeType[] values = values();
+ String[] labels = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ labels[i] = values[i].getLabel();
+ }
+ return labels;
+ }
+
+ public static NamingSchemeType fromLabel(String label) {
+ if (StringUtils.isEmpty(label)) {
+ return HOP_FIELD;
+ }
+ for (NamingSchemeType type : values()) {
+ if (type.getLabel().equals(label) || type.code.equalsIgnoreCase(label.trim())) {
+ return type;
+ }
+ }
+ return HOP_FIELD;
+ }
+}
diff --git a/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingWordSeparator.java b/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingWordSeparator.java
new file mode 100644
index 00000000000..0ecd0e2c881
--- /dev/null
+++ b/plugins/misc/naming/src/main/java/org/apache/hop/naming/metadata/NamingWordSeparator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.naming.metadata;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hop.i18n.BaseMessages;
+
+/** Character used to join words after splitting (ignored for camelCase / PascalCase styles). */
+public enum NamingWordSeparator {
+ EMPTY("empty", "", "NamingWordSeparator.Empty"),
+ UNDERSCORE("underscore", "_", "NamingWordSeparator.Underscore"),
+ DASH("dash", "-", "NamingWordSeparator.Dash");
+
+ private static final Class> PKG = NamingScheme.class;
+
+ private final String code;
+ private final String value;
+ private final String i18nKey;
+
+ NamingWordSeparator(String code, String value, String i18nKey) {
+ this.code = code;
+ this.value = value;
+ this.i18nKey = i18nKey;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getLabel() {
+ return BaseMessages.getString(PKG, i18nKey);
+ }
+
+ public static NamingWordSeparator fromCode(String code) {
+ if (StringUtils.isEmpty(code)) {
+ return UNDERSCORE;
+ }
+ for (NamingWordSeparator sep : values()) {
+ if (sep.code.equalsIgnoreCase(code.trim())) {
+ return sep;
+ }
+ }
+ return UNDERSCORE;
+ }
+
+ public static String[] getLabels() {
+ NamingWordSeparator[] values = values();
+ String[] labels = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ labels[i] = values[i].getLabel();
+ }
+ return labels;
+ }
+
+ public static NamingWordSeparator fromLabel(String label) {
+ if (StringUtils.isEmpty(label)) {
+ return UNDERSCORE;
+ }
+ for (NamingWordSeparator sep : values()) {
+ if (sep.getLabel().equals(label) || sep.code.equalsIgnoreCase(label.trim())) {
+ return sep;
+ }
+ }
+ return UNDERSCORE;
+ }
+}
diff --git a/plugins/misc/naming/src/main/resources/naming.svg b/plugins/misc/naming/src/main/resources/naming.svg
new file mode 100644
index 00000000000..8c693fbbf0d
--- /dev/null
+++ b/plugins/misc/naming/src/main/resources/naming.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/plugins/misc/naming/src/main/resources/org/apache/hop/naming/gui/messages/messages_en_US.properties b/plugins/misc/naming/src/main/resources/org/apache/hop/naming/gui/messages/messages_en_US.properties
new file mode 100644
index 00000000000..0bff16b34aa
--- /dev/null
+++ b/plugins/misc/naming/src/main/resources/org/apache/hop/naming/gui/messages/messages_en_US.properties
@@ -0,0 +1,28 @@
+#
+#
+# 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.
+#
+#
+
+Naming.Toolbar.Apply.ToolTip=Apply a Naming Scheme to a column
+Naming.ColumnSelection.Title=Select column
+Naming.ColumnSelection.Message=Select the column whose values should be renamed
+Naming.SchemeSelection.Title=Select Naming Scheme
+Naming.SchemeSelection.Message=Select the Naming Scheme to apply (type\: Hop field names)
+Naming.NoSchemes.Title=No Naming Schemes
+Naming.NoSchemes.Message=There are no Naming Scheme metadata elements of type "Hop field names".\nCreate one in the Metadata perspective first.
+Naming.Error.Title=Error
+Naming.Error.Message=Error applying Naming Scheme
diff --git a/plugins/misc/naming/src/main/resources/org/apache/hop/naming/metadata/messages/messages_en_US.properties b/plugins/misc/naming/src/main/resources/org/apache/hop/naming/metadata/messages/messages_en_US.properties
new file mode 100644
index 00000000000..73903c5662e
--- /dev/null
+++ b/plugins/misc/naming/src/main/resources/org/apache/hop/naming/metadata/messages/messages_en_US.properties
@@ -0,0 +1,51 @@
+#
+#
+# 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.
+#
+#
+
+NamingScheme.Name=Naming Scheme
+NamingScheme.Description=Reusable rules to normalize names for Hop fields, database tables/columns, and files/folders
+
+NamingSchemeEditor.Name.Label=Name
+NamingSchemeEditor.Description.Label=Description
+NamingSchemeEditor.Type.Label=Type
+NamingSchemeEditor.CaseStyle.Label=Case style
+NamingSchemeEditor.WordSeparator.Label=Word separator
+NamingSchemeEditor.ExtraDelimiters.Label=Extra delimiters
+NamingSchemeEditor.RemoveSpecialCharacters.Label=Remove special characters
+NamingSchemeEditor.CollapseSeparators.Label=Collapse repeated separators
+NamingSchemeEditor.TrimEdgeSeparators.Label=Trim leading/trailing separators
+NamingSchemeEditor.Prefix.Label=Prefix
+NamingSchemeEditor.Suffix.Label=Suffix
+NamingSchemeEditor.PreviewInput.Label=Preview input
+NamingSchemeEditor.PreviewOutput.Label=Preview output
+
+NamingSchemeType.HopField=Hop field names
+NamingSchemeType.DatabaseTable=Database tables
+NamingSchemeType.DatabaseColumn=Database columns
+NamingSchemeType.File=Files
+NamingSchemeType.Folder=Folders
+
+NamingCaseStyle.AsIs=As is
+NamingCaseStyle.Lower=Lower case
+NamingCaseStyle.Upper=Upper case
+NamingCaseStyle.Camel=camelCase
+NamingCaseStyle.Pascal=PascalCase
+
+NamingWordSeparator.Empty=None (empty)
+NamingWordSeparator.Underscore=Underscore (_)
+NamingWordSeparator.Dash=Dash (-)
diff --git a/plugins/misc/naming/src/test/java/org/apache/hop/naming/engine/NamingEngineTest.java b/plugins/misc/naming/src/test/java/org/apache/hop/naming/engine/NamingEngineTest.java
new file mode 100644
index 00000000000..97e1dc38e95
--- /dev/null
+++ b/plugins/misc/naming/src/test/java/org/apache/hop/naming/engine/NamingEngineTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.naming.engine;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.hop.naming.metadata.NamingCaseStyle;
+import org.apache.hop.naming.metadata.NamingScheme;
+import org.apache.hop.naming.metadata.NamingWordSeparator;
+import org.junit.jupiter.api.Test;
+
+class NamingEngineTest {
+
+ @Test
+ void nullAndEmpty() {
+ NamingScheme scheme = lowerUnderscore();
+ assertNull(NamingEngine.apply(scheme, null));
+ assertEquals("", NamingEngine.apply(scheme, ""));
+ }
+
+ @Test
+ void orderIdLowerUnderscore() {
+ assertEquals("order_id", NamingEngine.apply(lowerUnderscore(), "Order ID"));
+ }
+
+ @Test
+ void orderIdAsIsUnderscore() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.AS_IS.getCode());
+ scheme.setWordSeparator(NamingWordSeparator.UNDERSCORE.getCode());
+ scheme.setRemoveSpecialCharacters(false);
+ assertEquals("Order_ID", NamingEngine.apply(scheme, "Order ID"));
+ }
+
+ @Test
+ void orderIdCamel() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.CAMEL.getCode());
+ scheme.setRemoveSpecialCharacters(false);
+ assertEquals("orderId", NamingEngine.apply(scheme, "Order ID"));
+ }
+
+ @Test
+ void orderIdPascal() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.PASCAL.getCode());
+ scheme.setRemoveSpecialCharacters(false);
+ assertEquals("OrderId", NamingEngine.apply(scheme, "Order ID"));
+ }
+
+ @Test
+ void snakeToCamel() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.CAMEL.getCode());
+ assertEquals("orderId", NamingEngine.apply(scheme, "order_id"));
+ }
+
+ @Test
+ void kebabToUpperSnake() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.UPPER.getCode());
+ scheme.setWordSeparator(NamingWordSeparator.UNDERSCORE.getCode());
+ assertEquals("ORDER_ID", NamingEngine.apply(scheme, "order-id"));
+ }
+
+ @Test
+ void collapseAndTrimWhitespace() {
+ assertEquals("foo_bar", NamingEngine.apply(lowerUnderscore(), " foo bar "));
+ }
+
+ @Test
+ void removeSpecialCharacters() {
+ NamingScheme scheme = lowerUnderscore();
+ scheme.setRemoveSpecialCharacters(true);
+ scheme.setExtraDelimiters("#");
+ assertEquals("field_name", NamingEngine.apply(scheme, "field#name!"));
+ }
+
+ @Test
+ void dashSeparator() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.LOWER.getCode());
+ scheme.setWordSeparator(NamingWordSeparator.DASH.getCode());
+ assertEquals("order-id", NamingEngine.apply(scheme, "Order ID"));
+ }
+
+ @Test
+ void prefixAndSuffix() {
+ NamingScheme scheme = lowerUnderscore();
+ scheme.setPrefix("fld_");
+ scheme.setSuffix("_x");
+ assertEquals("fld_order_id_x", NamingEngine.apply(scheme, "Order ID"));
+ }
+
+ @Test
+ void camelCaseInputSplit() {
+ NamingScheme scheme = lowerUnderscore();
+ assertEquals("order_id", NamingEngine.apply(scheme, "orderId"));
+ }
+
+ @Test
+ void nullSchemeUsesDefaults() {
+ assertEquals("order_id", NamingEngine.apply(null, "Order ID"));
+ }
+
+ private static NamingScheme lowerUnderscore() {
+ NamingScheme scheme = new NamingScheme();
+ scheme.setCaseStyle(NamingCaseStyle.LOWER.getCode());
+ scheme.setWordSeparator(NamingWordSeparator.UNDERSCORE.getCode());
+ scheme.setRemoveSpecialCharacters(true);
+ scheme.setCollapseRepeatedSeparators(true);
+ scheme.setTrimEdgeSeparators(true);
+ return scheme;
+ }
+}
diff --git a/plugins/misc/pom.xml b/plugins/misc/pom.xml
index 83374e5c95a..a6b5122f120 100644
--- a/plugins/misc/pom.xml
+++ b/plugins/misc/pom.xml
@@ -37,6 +37,7 @@
import
mail
marketplace
+ naming
passwords
projects
py4j
diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java b/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
index 96f2ba39df3..daf14df1a72 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
@@ -4229,6 +4229,59 @@ public void setText(String text, int colNr, int rowNr) {
item.setText(colNr, text);
}
+ /**
+ * Replace cell values in one column for the given rows and record a single undo step.
+ *
+ * {@code colNr} is the {@link TableItem} column index (1 = first {@link ColumnInfo}; 0 is the
+ * leading row-number column).
+ *
+ * @param colNr table item column index
+ * @param rowIndices absolute row indices into {@link #table}
+ * @param newValues new cell values, same length as {@code rowIndices}
+ */
+ public void applyColumnValues(int colNr, int[] rowIndices, String[] newValues) {
+ if (readonly || rowIndices == null || newValues == null || rowIndices.length == 0) {
+ return;
+ }
+ if (rowIndices.length != newValues.length) {
+ throw new IllegalArgumentException(
+ "rowIndices and newValues must have the same length ("
+ + rowIndices.length
+ + " vs "
+ + newValues.length
+ + ")");
+ }
+ if (colNr < 1 || colNr >= table.getColumnCount()) {
+ return;
+ }
+
+ applyAllChanges();
+
+ int size = rowIndices.length;
+ String[][] before = new String[size][];
+ String[][] after = new String[size][];
+ int[] index = new int[size];
+
+ for (int i = 0; i < size; i++) {
+ int rowNr = rowIndices[i];
+ if (rowNr < 0 || rowNr >= table.getItemCount()) {
+ continue;
+ }
+ TableItem item = table.getItem(rowNr);
+ index[i] = rowNr;
+ before[i] = getItemText(item);
+ item.setText(colNr, Const.NVL(newValues[i], ""));
+ after[i] = getItemText(item);
+ }
+
+ if (undoEnabled) {
+ ChangeAction ta = new ChangeAction();
+ ta.setChanged(before, after, index);
+ addUndo(ta);
+ }
+ setModified();
+ }
+
/**
* @param sortable the sortable to set
*/