From 4012db562a93069b210e81f776b7ffafccac98ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Thu, 18 Dec 2025 16:50:07 +0100 Subject: [PATCH] Cleanup Preference Handling in Cucumber Editor --- .../META-INF/MANIFEST.MF | 2 + .../CucumberRunCodeMiningProvider.java | 4 +- .../eclipse/editor/launching/Mode.java | 14 --- .../CucumberEditorPreferences.java | 112 +++++++++++++++++ .../CucumberPreferenceInitializer.java | 15 +-- .../preferences/CucumberPreferencePage.java | 43 +------ .../ICucumberPreferenceConstants.java | 53 -------- .../editor/preferences/StepPreferences.java | 37 ------ .../properties/CucumberEditorProperties.java | 81 ++++++++++++- .../properties/CucumberPropertiesPage.java | 17 +-- .../preferences/CucumberJavaPreferences.java | 82 ++++++++++--- .../CucumberJavaBackendProperties.java | 113 +++++++++++++++++- 12 files changed, 385 insertions(+), 188 deletions(-) create mode 100644 io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberEditorPreferences.java delete mode 100644 io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/ICucumberPreferenceConstants.java delete mode 100644 io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/StepPreferences.java diff --git a/io.cucumber.eclipse.editor/META-INF/MANIFEST.MF b/io.cucumber.eclipse.editor/META-INF/MANIFEST.MF index b0762e1e..4871e73f 100644 --- a/io.cucumber.eclipse.editor/META-INF/MANIFEST.MF +++ b/io.cucumber.eclipse.editor/META-INF/MANIFEST.MF @@ -47,6 +47,8 @@ Export-Package: io.cucumber.eclipse.editor, io.cucumber.eclipse.editor.hyperlinks, io.cucumber.eclipse.editor.launching, io.cucumber.eclipse.editor.marker, + io.cucumber.eclipse.editor.preferences;x-internal:=true, + io.cucumber.eclipse.editor.properties;x-internal:=true, io.cucumber.eclipse.editor.steps, io.cucumber.eclipse.editor.syntaxhighlight Import-Package: org.apache.commons.io;version="[2.16.0,3.0.0)", diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/codemining/CucumberRunCodeMiningProvider.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/codemining/CucumberRunCodeMiningProvider.java index 3493eed1..5cb1284d 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/codemining/CucumberRunCodeMiningProvider.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/codemining/CucumberRunCodeMiningProvider.java @@ -28,6 +28,7 @@ import io.cucumber.eclipse.editor.document.GherkinEditorDocument; import io.cucumber.eclipse.editor.launching.ILauncher; import io.cucumber.eclipse.editor.launching.Mode; +import io.cucumber.eclipse.editor.preferences.CucumberEditorPreferences; import io.cucumber.messages.types.Feature; import io.cucumber.messages.types.Location; import io.cucumber.messages.types.Scenario; @@ -53,8 +54,9 @@ public CompletableFuture> provideCodeMinings(ITextVi return Collections.emptyList(); } List list = new ArrayList<>(); + CucumberEditorPreferences preferences = CucumberEditorPreferences.of(editorDocument.getResource()); for (Mode mode : Mode.values()) { - if (!mode.showShortcut(editorDocument.getResource())) { + if (!preferences.isShowShortcutFor(mode)) { continue; } for (ILauncher launcher : CucumberServiceRegistry.getLauncher()) { diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/launching/Mode.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/launching/Mode.java index 1ed74a34..77ac2cc9 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/launching/Mode.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/launching/Mode.java @@ -1,14 +1,9 @@ package io.cucumber.eclipse.editor.launching; -import org.eclipse.core.resources.IResource; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.ILaunchMode; -import io.cucumber.eclipse.editor.Activator; -import io.cucumber.eclipse.editor.preferences.ICucumberPreferenceConstants; -import io.cucumber.eclipse.editor.properties.CucumberEditorProperties; - public enum Mode { RUN, DEBUG, PROFILE; @@ -77,13 +72,4 @@ public String getSymbol() { } } - - public boolean showShortcut(IResource resource) { - CucumberEditorProperties properties = CucumberEditorProperties.of(resource); - if (properties.isEnabled()) { - return properties.isShowShortcutFor(this); - } - return Activator.getDefault().getPreferenceStore() - .getBoolean(ICucumberPreferenceConstants.PREF_SHOW_RUN_SHORTCUT_PREFIX + name()); - } } \ No newline at end of file diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberEditorPreferences.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberEditorPreferences.java new file mode 100644 index 00000000..489d7a24 --- /dev/null +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberEditorPreferences.java @@ -0,0 +1,112 @@ +package io.cucumber.eclipse.editor.preferences; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.jface.preference.IPreferenceStore; + +import io.cucumber.eclipse.editor.Activator; +import io.cucumber.eclipse.editor.launching.Mode; +import io.cucumber.eclipse.editor.properties.CucumberEditorProperties; + +/** + * Provides unified access to Cucumber editor preferences and properties. + *

+ * This record combines workspace-level preferences with optional project-specific + * overrides. When project-specific settings are enabled for a resource, those + * values take precedence over workspace preferences. + *

+ *

+ * Usage: + *

+ * // Workspace preferences only
+ * CucumberEditorPreferences prefs = CucumberEditorPreferences.of();
+ * 
+ * // With project override support
+ * CucumberEditorPreferences prefs = CucumberEditorPreferences.of(resource);
+ * if (prefs.isShowShortcutFor(Mode.RUN)) {
+ *     // display run shortcut...
+ * }
+ * 
+ *

+ * + * @param store the workspace preference store + * @param node the project-specific preferences node, or null if using workspace preferences + * + * @see CucumberEditorProperties for direct project property access + */ +public final record CucumberEditorPreferences(IPreferenceStore store, IEclipsePreferences node) { + + static final String PREF_SHOW_RUN_SHORTCUT_PREFIX = Activator.PLUGIN_ID + ".show_run_shortcut_"; + + /** + * Creates a preferences instance using workspace settings only. + * + * @return a preferences instance with workspace-level settings + */ + public static CucumberEditorPreferences of() { + return of(null); + } + + /** + * Creates a preferences instance with optional project override support. + *

+ * If the resource's project has project-specific settings enabled, those + * values will be returned. Otherwise, workspace preferences are used. + *

+ * + * @param resource the resource whose project may override preferences, or null for workspace only + * @return a preferences instance with appropriate settings + */ + public static CucumberEditorPreferences of(IResource resource) { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + if (resource != null) { + CucumberEditorProperties properties = CucumberEditorProperties.of(resource); + if (properties.isEnabled()) { + // project settings overwrite preferences... + return new CucumberEditorPreferences(store, properties.node()); + } + } + return new CucumberEditorPreferences(store, null); + } + + /** + * Checks if the launch shortcut should be shown for the given mode. + *

+ * If project-specific settings are active, returns the project value. + * Otherwise, returns the workspace preference. + *

+ * + * @param mode the launch mode (RUN, DEBUG, PROFILE) + * @return true if the shortcut should be shown, false otherwise + */ + public boolean isShowShortcutFor(Mode mode) { + if (node != null) { + // Use project-specific setting + return node.getBoolean(CucumberEditorProperties.KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + mode.name(), true); + } + // Use workspace preference + return store.getBoolean(PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name()); + } + + /** + * Sets the workspace preference for showing the launch shortcut for the given mode. + * + * @param mode the launch mode (RUN, DEBUG, PROFILE) + * @param show true to show the shortcut, false to hide it + */ + public static void setShowShortcutFor(Mode mode, boolean show) { + IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + setShowShortcutFor(store, mode, show); + } + + /** + * Sets the preference for showing the launch shortcut in the given preference store. + * + * @param store the preference store to update + * @param mode the launch mode (RUN, DEBUG, PROFILE) + * @param show true to show the shortcut, false to hide it + */ + protected static void setShowShortcutFor(IPreferenceStore store, Mode mode, boolean show) { + store.setValue(PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name(), show); + } +} diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferenceInitializer.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferenceInitializer.java index bfc644ec..a5c14eb1 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferenceInitializer.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferenceInitializer.java @@ -5,25 +5,14 @@ import io.cucumber.eclipse.editor.Activator; import io.cucumber.eclipse.editor.launching.Mode; -import io.cucumber.eclipse.editor.preferences.ICucumberPreferenceConstants.CucumberIndentationStyle; -public class CucumberPreferenceInitializer extends - AbstractPreferenceInitializer { +public class CucumberPreferenceInitializer extends AbstractPreferenceInitializer { @Override public void initializeDefaultPreferences() { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); - store.setDefault(StepPreferences.PREF_CHECK_STEP_DEFINITIONS, true); - store.setDefault(ICucumberPreferenceConstants.PREF_FORMAT_RIGHT_ALIGN_NUMERIC_VALUES_IN_TABLES, true); - store.setDefault(ICucumberPreferenceConstants.PREF_FORMAT_PRESERVE_BLANK_LINE_BETWEEN_STEPS, false); - store.setDefault(ICucumberPreferenceConstants.PREF_FORMAT_CENTER_STEPS, false); - store.setDefault(ICucumberPreferenceConstants.PREF_INDENTATION_STYLE, - CucumberIndentationStyle.TWO_SPACES.getValue()); - - //#239:Only match step implementation in same package as feature file - store.setDefault(StepPreferences.PREF_GLUE_ONLY_IN_SAME_LOCATION, false); for (Mode mode : Mode.values()) { - store.setDefault(ICucumberPreferenceConstants.PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name(), true); + store.setDefault(CucumberEditorPreferences.PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name(), true); } } diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferencePage.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferencePage.java index 0099d5f2..fabff70a 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferencePage.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/CucumberPreferencePage.java @@ -16,9 +16,11 @@ public class CucumberPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + public CucumberPreferencePage() { super(FLAT); - setPreferenceStore(Activator.getDefault().getPreferenceStore()); + CucumberEditorPreferences editorPreferences = CucumberEditorPreferences.of(); + setPreferenceStore(editorPreferences.store()); } @Override @@ -28,47 +30,10 @@ protected void createFieldEditors() { for (Mode mode : Mode.values()) { addField(new BooleanFieldEditor( - ICucumberPreferenceConstants.PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name(), + CucumberEditorPreferences.PREF_SHOW_RUN_SHORTCUT_PREFIX + mode.name(), getLabelForMode(mode), parent)); } - -// CLabel label = new CLabel(parent, SWT.NULL); -// label.setText(getString("Plugin Settings")); -// label.setImage(getImage("icons/cukes.gif")); - - -// addField(new BooleanFieldEditor( -// StepPreferences.PREF_CHECK_STEP_DEFINITIONS, -// getString("&Enable step definitions glue detection"), parent)); -// -// //#239:Only match step implementation in same package as feature file -// addField(new BooleanFieldEditor( -// StepPreferences.PREF_GLUE_ONLY_IN_SAME_LOCATION, -// getString("&Glue only gherkins and step definitions files in the same location"), getFieldEditorParent())); -// - -// parent = getFieldEditorParent(); -// -// label = new CLabel(parent, SWT.NULL); -// label.setText(getString("Gherkin Formatting")); -// label.setImage(getImage("icons/cukes.gif")); -// -// addField(new BooleanFieldEditor( -// ICucumberPreferenceConstants.PREF_FORMAT_RIGHT_ALIGN_NUMERIC_VALUES_IN_TABLES, -// getString("&Right-align numeric values in tables"), parent)); -// -// addField(new BooleanFieldEditor( -// ICucumberPreferenceConstants.PREF_FORMAT_CENTER_STEPS, -// getString("&Center Steps"), getFieldEditorParent())); -// -// addField(new BooleanFieldEditor( -// ICucumberPreferenceConstants.PREF_FORMAT_PRESERVE_BLANK_LINE_BETWEEN_STEPS, -// getString("&Preserve blank lines between steps"), getFieldEditorParent())); -// -// addField(new ComboFieldEditor(ICucumberPreferenceConstants.PREF_INDENTATION_STYLE, -// getString("&Indentation Style:"), CucumberIndentationStyle.getLabelsAndValues(), -// getFieldEditorParent())); } public static String getLabelForMode(Mode mode) { diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/ICucumberPreferenceConstants.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/ICucumberPreferenceConstants.java deleted file mode 100644 index 57de5edc..00000000 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/ICucumberPreferenceConstants.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.cucumber.eclipse.editor.preferences; - -import io.cucumber.eclipse.editor.Activator; - -public interface ICucumberPreferenceConstants { - - public static final String _PREFIX = Activator.PLUGIN_ID + "."; //$NON-NLS-1$ - - // Gherkin Formatting - public static final String PREF_FORMAT_RIGHT_ALIGN_NUMERIC_VALUES_IN_TABLES = - _PREFIX + "format_right_align_numeric_values_in_tables"; //$NON-NLS-1$ - public static final String PREF_FORMAT_CENTER_STEPS = - _PREFIX + "format_center_steps"; //$NON-NLS-1$ - public static final String PREF_FORMAT_PRESERVE_BLANK_LINE_BETWEEN_STEPS = - _PREFIX + "format_preserve_blank_line_between_steps"; //$NON-NLS-1$ - public static final String PREF_INDENTATION_STYLE = - _PREFIX + "indentation_style"; //$NON-NLS-1$ - - public static final String PREF_SHOW_RUN_SHORTCUT_PREFIX = _PREFIX + "show_run_shortcut_"; //$NON-NLS-1$ - - public static enum CucumberIndentationStyle { - TWO_SPACES("2 Spaces", " "), - FOUR_SPACES("4 Spaces", " "), - TABS("Tabs", " "); - - private final String label; - private final String value; - - private CucumberIndentationStyle(String label, String value) { - this.label = label; - this.value = value; - } - - public static String[][] getLabelsAndValues() { - CucumberIndentationStyle[] values = values(); - String[][] labelsAndValues = new String[values.length][2]; - for (int i = 0; i < values.length; i++) { - CucumberIndentationStyle value = values[i]; - labelsAndValues[i][0] = value.getLabel(); - labelsAndValues[i][1] = value.getValue(); - } - return labelsAndValues; - } - - public String getLabel() { - return label; - } - - public String getValue() { - return value; - } - } -} diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/StepPreferences.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/StepPreferences.java deleted file mode 100644 index e3210544..00000000 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/preferences/StepPreferences.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.cucumber.eclipse.editor.preferences; - -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.preferences.IPreferencesService; - -public class StepPreferences { - - public static StepPreferences INSTANCE = new StepPreferences(); - - // Plugin Settings - public static String PREF_CHECK_STEP_DEFINITIONS = "check_step_definitions"; //$NON-NLS-1$ - // #239:Only match step implementation in same package as feature file - public static String PREF_GLUE_ONLY_IN_SAME_LOCATION = "glue_only_in_same_location"; //$NON-NLS-1$ - // Newly Declared By Girija for User-Settings Cucumber Preference Page - public static String PREF_ADD_PACKAGE = "add_package"; //$NON-NLS-1$ - - private IPreferencesService preferencesService; - - private StepPreferences() { - preferencesService = Platform.getPreferencesService(); - } - - public boolean isStepDefinitionsMatchingEnabled() { - return preferencesService.getBoolean("cucumber.eclipse.editor", PREF_CHECK_STEP_DEFINITIONS, false, null); - } - - // Get Package Name(s) From User-Settings Page - public String getPackageName() { - return preferencesService.getString("cucumber.eclipse.editor", PREF_ADD_PACKAGE, "", null); - } - - // #239:Only match step implementation in same package as feature file - public Boolean isGlueOnlyInSameLocationEnabled() { - return preferencesService.getBoolean("cucumber.eclipse.editor", PREF_GLUE_ONLY_IN_SAME_LOCATION, false, null); - } - -} diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberEditorProperties.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberEditorProperties.java index 05a188c4..562b5f85 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberEditorProperties.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberEditorProperties.java @@ -7,12 +7,43 @@ import io.cucumber.eclipse.editor.launching.Mode; +/** + * Provides access to project-specific properties for the Cucumber editor. + *

+ * This record encapsulates project-scoped settings stored in the Eclipse preferences + * system. Properties are stored under the {@code io.cucumber.eclipse.editor} namespace + * and can be configured via the project properties page. + *

+ *

+ * Usage: + *

+ * CucumberEditorProperties props = CucumberEditorProperties.of(resource);
+ * if (props.isEnabled()) {
+ *     boolean showRun = props.isShowShortcutFor(Mode.RUN);
+ * }
+ * 
+ *

+ * + * @param node the Eclipse preferences node for project-specific settings, or null if not available + * + * @see CucumberEditorPreferences for workspace-wide preferences with project override support + */ public record CucumberEditorProperties(IEclipsePreferences node) { private static final String NAMESPACE = "io.cucumber.eclipse.editor"; - private static final String KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS = "enableProjectSpecific"; - private static final String KEY_SHOW_LAUNCH_SHORTCUT_PREFIX = "showShortcut"; + + /** Key for enabling project-specific settings */ + static final String KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS = "enableProjectSpecific"; + + /** Key prefix for launch shortcut visibility settings (suffixed with mode name) */ + public static final String KEY_SHOW_LAUNCH_SHORTCUT_PREFIX = "showShortcut"; + /** + * Creates a properties instance for the given resource. + * + * @param resource the resource whose project properties should be accessed, or null + * @return a properties instance with the project's preference node, or with null node if resource is null + */ public static CucumberEditorProperties of(IResource resource) { if (resource == null) { return new CucumberEditorProperties(null); @@ -21,6 +52,11 @@ public static CucumberEditorProperties of(IResource resource) { return new CucumberEditorProperties(node); } + /** + * Checks if project-specific settings are enabled. + * + * @return true if project-specific settings are enabled, false otherwise + */ public boolean isEnabled() { if (node == null) { return false; @@ -28,6 +64,15 @@ public boolean isEnabled() { return node.getBoolean(KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS, false); } + /** + * Enables or disables project-specific settings. + *

+ * When enabled, project properties override workspace preferences. + * This method automatically calls {@link #flush()} to persist the change. + *

+ * + * @param enabled true to enable project-specific settings, false to use workspace defaults + */ public void setEnabled(boolean enabled) { if (node == null) { return; @@ -36,6 +81,13 @@ public void setEnabled(boolean enabled) { flush(); } + /** + * Persists all pending changes to the backing store. + *

+ * This method should be called after making property changes to ensure + * they are saved. Exceptions during flush are silently ignored. + *

+ */ public void flush() { if (node == null) { return; @@ -46,12 +98,24 @@ public void flush() { } } + /** + * Gets the Eclipse preferences node for the given resource's project. + * + * @param resource the resource whose project node should be retrieved + * @return the preferences node for the project + */ static IEclipsePreferences getNode(IResource resource) { ProjectScope scope = new ProjectScope(resource.getProject()); IEclipsePreferences node = scope.getNode(NAMESPACE); return node; } + /** + * Checks if the launch shortcut should be shown for the given mode. + * + * @param mode the launch mode (RUN, DEBUG, PROFILE) + * @return true if the shortcut should be shown, false otherwise; defaults to true + */ public boolean isShowShortcutFor(Mode mode) { if (node == null) { return true; @@ -59,10 +123,19 @@ public boolean isShowShortcutFor(Mode mode) { return node.getBoolean(KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + mode.name(), true); } - public void setShowShortcutFor(Mode mode, boolean selection) { + /** + * Sets whether the launch shortcut should be shown for the given mode. + *

+ * Call {@link #flush()} to persist the change. + *

+ * + * @param mode the launch mode (RUN, DEBUG, PROFILE) + * @param show true to show the shortcut, false to hide it + */ + public void setShowShortcutFor(Mode mode, boolean show) { if (node == null) { return; } - node.putBoolean(KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + mode.name(), selection); + node.putBoolean(KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + mode.name(), show); } } diff --git a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberPropertiesPage.java b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberPropertiesPage.java index d518d2d1..2e34cebd 100644 --- a/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberPropertiesPage.java +++ b/io.cucumber.eclipse.editor/src/io/cucumber/eclipse/editor/properties/CucumberPropertiesPage.java @@ -5,6 +5,7 @@ import java.util.Map.Entry; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; @@ -12,7 +13,6 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.PropertyPage; import io.cucumber.eclipse.editor.Images; @@ -21,7 +21,6 @@ public class CucumberPropertiesPage extends PropertyPage { - private Text validationPlugins; private Map modeButtons = new HashMap<>(); private Button enableProjectSpecific; @@ -38,10 +37,11 @@ protected Control createContents(Composite parent) { GridLayout layout = new GridLayout(); layout.numColumns = 1; composite.setLayout(layout); - CucumberEditorProperties properties = CucumberEditorProperties.of(getResource()); + IResource resource = getResource(); + IEclipsePreferences node = CucumberEditorProperties.getNode(resource); enableProjectSpecific = new Button(composite, SWT.CHECK); enableProjectSpecific.setText("Enable project specific settings"); - enableProjectSpecific.setSelection(properties.isEnabled()); + enableProjectSpecific.setSelection(node.getBoolean(CucumberEditorProperties.KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS, false)); enableProjectSpecific.addSelectionListener(new SelectionListener() { @Override @@ -56,7 +56,7 @@ public void widgetDefaultSelected(SelectionEvent e) { }); for (Mode mode : Mode.values()) { Button button = new Button(composite, SWT.CHECK); - button.setSelection(properties.isShowShortcutFor(mode)); + button.setSelection(node.getBoolean(CucumberEditorProperties.KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + mode.name(), true)); button.setText(CucumberPreferencePage.getLabelForMode(mode)); modeButtons.put(mode, button); } @@ -77,11 +77,12 @@ private IResource getResource() { @Override public boolean performOk() { - CucumberEditorProperties properties = CucumberEditorProperties.of(getResource()); - properties.setEnabled(enableProjectSpecific.getSelection()); + IEclipsePreferences node = CucumberEditorProperties.getNode(getResource()); + node.putBoolean(CucumberEditorProperties.KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS, enableProjectSpecific.getSelection()); for (Entry entry : modeButtons.entrySet()) { - properties.setShowShortcutFor(entry.getKey(), entry.getValue().getSelection()); + node.putBoolean(CucumberEditorProperties.KEY_SHOW_LAUNCH_SHORTCUT_PREFIX + entry.getKey().name(), entry.getValue().getSelection()); } + CucumberEditorProperties properties = new CucumberEditorProperties(node); properties.flush(); return true; } diff --git a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/preferences/CucumberJavaPreferences.java b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/preferences/CucumberJavaPreferences.java index 109f17b6..c2d9b634 100644 --- a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/preferences/CucumberJavaPreferences.java +++ b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/preferences/CucumberJavaPreferences.java @@ -11,6 +11,35 @@ import io.cucumber.eclipse.java.Activator; import io.cucumber.eclipse.java.properties.CucumberJavaBackendProperties; +/** + * Provides unified access to Cucumber Java backend preferences and properties. + *

+ * This record combines workspace-level preferences with optional project-specific + * overrides. When project-specific settings are enabled for a resource, those + * values take precedence over workspace preferences. + *

+ *

+ * Usage: + *

+ * // Workspace preferences only
+ * CucumberJavaPreferences prefs = CucumberJavaPreferences.of();
+ * 
+ * // With project override support
+ * CucumberJavaPreferences prefs = CucumberJavaPreferences.of(resource);
+ * if (prefs.showHooks()) {
+ *     // display hooks...
+ * }
+ * 
+ *

+ * + * @param store the workspace preference store + * @param node the project-specific preferences node, or null if using workspace preferences + * @param showHooks whether to show hook annotations in feature files + * @param glueFilter list of active glue code package filters + * @param plugins list of validation plugin class names + * + * @see CucumberJavaBackendProperties for direct project property access + */ public final record CucumberJavaPreferences(IPreferenceStore store, IEclipsePreferences node, boolean showHooks, List glueFilter, List plugins) { @@ -20,10 +49,25 @@ public final record CucumberJavaPreferences(IPreferenceStore store, IEclipsePref static final String PREF_INACTIVE_FILTERS_LIST = Activator.PLUGIN_ID + ".inactive_filters"; static final String PREF_SHOW_HOOK_ANNOTATIONS = Activator.PLUGIN_ID + ".show_hooks"; + /** + * Creates a preferences instance using workspace settings only. + * + * @return a preferences instance with workspace-level settings + */ public static CucumberJavaPreferences of() { return of(null); } + /** + * Creates a preferences instance with optional project override support. + *

+ * If the resource's project has project-specific settings enabled, those + * values will be returned. Otherwise, workspace preferences are used. + *

+ * + * @param resource the resource whose project may override preferences, or null for workspace only + * @return a preferences instance with appropriate settings + */ public static CucumberJavaPreferences of(IResource resource) { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); if (resource != null) { @@ -40,19 +84,17 @@ public static CucumberJavaPreferences of(IResource resource) { } /** - * Parses the comma separated string into an array of strings + * Parses a comma-separated string into a list of strings. * - * @param listString a string representation of a list of elements separated by - * commas - * - * @return an array of string + * @param listString a string representation of a list of elements separated by commas + * @return a list of strings, empty if the input is null or blank */ static List parseList(String listString) { if (listString == null || listString.isBlank()) { return List.of(); } List list = new ArrayList(10); - StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$ + StringTokenizer tokenizer = new StringTokenizer(listString, ","); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); list.add(token); @@ -61,14 +103,14 @@ static List parseList(String listString) { } /** - * Serializes the array of strings into one comma separated string. + * Serializes an array of strings into a comma-separated string. * - * @param list array of strings - * @return a single string composed of the given list + * @param list array of strings to serialize + * @return a comma-separated string, empty string if the array is null */ static String serializeList(String[] list) { if (list == null) { - return ""; //$NON-NLS-1$ + return ""; } StringBuilder buffer = new StringBuilder(); for (int i = 0; i < list.length; i++) { @@ -80,13 +122,23 @@ static String serializeList(String[] list) { return buffer.toString(); } - public static void setShowHooks(boolean showhooks) { + /** + * Sets the workspace preference for showing hook annotations. + * + * @param showHooks true to show hook annotations, false to hide them + */ + public static void setShowHooks(boolean showHooks) { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); - setShowHooks(store, showhooks); + setShowHooks(store, showHooks); } - - protected static void setShowHooks(IPreferenceStore store, boolean showhooks) { - store.setValue(CucumberJavaPreferences.PREF_SHOW_HOOK_ANNOTATIONS, showhooks); + /** + * Sets the preference for showing hook annotations in the given preference store. + * + * @param store the preference store to update + * @param showHooks true to show hook annotations, false to hide them + */ + protected static void setShowHooks(IPreferenceStore store, boolean showHooks) { + store.setValue(CucumberJavaPreferences.PREF_SHOW_HOOK_ANNOTATIONS, showHooks); } } diff --git a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/properties/CucumberJavaBackendProperties.java b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/properties/CucumberJavaBackendProperties.java index 153f976e..a5c309cb 100644 --- a/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/properties/CucumberJavaBackendProperties.java +++ b/io.cucumber.eclipse.java/src/io/cucumber/eclipse/java/properties/CucumberJavaBackendProperties.java @@ -10,6 +10,27 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.osgi.service.prefs.BackingStoreException; +/** + * Provides access to project-specific properties for the Cucumber Java backend. + *

+ * This record encapsulates project-scoped settings stored in the Eclipse preferences + * system. Properties are stored under the {@code io.cucumber.eclipse.java} namespace + * and can be configured via the project properties page. + *

+ *

+ * Usage: + *

+ * CucumberJavaBackendProperties props = CucumberJavaBackendProperties.of(resource);
+ * if (props.isEnabled()) {
+ *     Stream<String> glueFilters = props.getGlueFilter();
+ * }
+ * 
+ *

+ * + * @param node the Eclipse preferences node for project-specific settings, or null if not available + * + * @see CucumberJavaPreferences for workspace-wide preferences with project override support + */ public final record CucumberJavaBackendProperties(IEclipsePreferences node) { static final String NAMESPACE = "io.cucumber.eclipse.java"; @@ -19,7 +40,12 @@ public final record CucumberJavaBackendProperties(IEclipsePreferences node) { static final String KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS = "enableProjectSpecific"; static final String KEY_SHOW_HOOK = "enableShowHook"; - + /** + * Creates a properties instance for the given resource. + * + * @param resource the resource whose project properties should be accessed, or null + * @return a properties instance with the project's preference node, or with null node if resource is null + */ public static CucumberJavaBackendProperties of(IResource resource) { if (resource == null) { return new CucumberJavaBackendProperties(null); @@ -28,6 +54,11 @@ public static CucumberJavaBackendProperties of(IResource resource) { return new CucumberJavaBackendProperties(node); } + /** + * Checks if project-specific settings are enabled. + * + * @return true if project-specific settings are enabled, false otherwise + */ public boolean isEnabled() { if (node == null) { return false; @@ -35,6 +66,15 @@ public boolean isEnabled() { return node.getBoolean(KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS, false); } + /** + * Enables or disables project-specific settings. + *

+ * When enabled, project properties override workspace preferences. + * Call {@link #flush()} to persist the change. + *

+ * + * @param enabled true to enable project-specific settings, false to use workspace defaults + */ public void setEnabled(boolean enabled) { if (node == null) { return; @@ -42,6 +82,13 @@ public void setEnabled(boolean enabled) { node.putBoolean(KEY_ENABLE_PROJECT_SPECIFIC_SETTINGS, enabled); } + /** + * Persists all pending changes to the backing store. + *

+ * This method should be called after making property changes to ensure + * they are saved. Exceptions during flush are silently ignored. + *

+ */ public void flush() { if (node == null) { return; @@ -52,6 +99,15 @@ public void flush() { } } + /** + * Gets the validation plugins configured for this project. + *

+ * Validation plugins are used during glue code validation regardless of + * feature file settings. The returned stream contains fully qualified class names. + *

+ * + * @return a stream of plugin class names, empty if none configured or node unavailable + */ public Stream getPlugins() { if (node == null) { return Stream.empty(); @@ -59,6 +115,14 @@ public Stream getPlugins() { return parseList(node.get(CucumberJavaBackendProperties.KEY_VALIDATION_PLUGINS, "")); } + /** + * Sets the validation plugins for this project. + *

+ * Call {@link #flush()} to persist the change. + *

+ * + * @param plugins a stream of fully qualified plugin class names + */ public void setPlugins(Stream plugins) { if (node == null) { return; @@ -66,6 +130,15 @@ public void setPlugins(Stream plugins) { node.put(CucumberJavaBackendProperties.KEY_VALIDATION_PLUGINS, plugins.collect(Collectors.joining(","))); } + /** + * Gets the active glue code package filters. + *

+ * Glue filters restrict which packages are scanned for step definitions, + * improving performance and reducing false matches. + *

+ * + * @return a stream of package filter patterns, empty if none configured or node unavailable + */ public Stream getGlueFilter() { if (node == null) { return Stream.empty(); @@ -73,13 +146,26 @@ public Stream getGlueFilter() { return parseList(node.get(CucumberJavaBackendProperties.KEY_ACTIVE_FILTER, "")); } - public void setGlueFilter(Stream plugins) { + /** + * Sets the active glue code package filters. + *

+ * Call {@link #flush()} to persist the change. + *

+ * + * @param filters a stream of package filter patterns + */ + public void setGlueFilter(Stream filters) { if (node == null) { return; } - node.put(CucumberJavaBackendProperties.KEY_ACTIVE_FILTER, plugins.collect(Collectors.joining(","))); + node.put(CucumberJavaBackendProperties.KEY_ACTIVE_FILTER, filters.collect(Collectors.joining(","))); } + /** + * Checks if hook annotations should be displayed in feature files. + * + * @return true if hook annotations should be shown, false otherwise + */ public boolean isShowHooks() { if (node == null) { return false; @@ -87,6 +173,14 @@ public boolean isShowHooks() { return node.getBoolean(KEY_SHOW_HOOK, false); } + /** + * Sets whether hook annotations should be displayed in feature files. + *

+ * Call {@link #flush()} to persist the change. + *

+ * + * @param show true to show hook annotations, false to hide them + */ public void setShowHooks(boolean show) { if (node == null) { return; @@ -94,14 +188,25 @@ public void setShowHooks(boolean show) { node.putBoolean(KEY_SHOW_HOOK, show); } + /** + * Gets the Eclipse preferences node for the given resource's project. + * + * @param resource the resource whose project node should be retrieved + * @return the preferences node for the project + */ static IEclipsePreferences getNode(IResource resource) { ProjectScope scope = new ProjectScope(resource.getProject()); IEclipsePreferences node = scope.getNode(CucumberJavaBackendProperties.NAMESPACE); return node; } + /** + * Parses a comma-separated list into a stream of non-blank strings. + * + * @param string the comma-separated string to parse + * @return a stream of trimmed, non-blank strings + */ static Stream parseList(String string) { return Arrays.stream(string.split(",")).map(String::trim).filter(Predicate.not(String::isBlank)); - } } \ No newline at end of file