Skip to content

Move ScopedPreferenceStore to org.eclipse.jface #4262 - #4270

Open
ptziegler wants to merge 3 commits into
eclipse-platform:masterfrom
ptziegler:issue4262
Open

Move ScopedPreferenceStore to org.eclipse.jface #4262#4270
ptziegler wants to merge 3 commits into
eclipse-platform:masterfrom
ptziegler:issue4262

Conversation

@ptziegler

Copy link
Copy Markdown
Contributor

The IPreferenceStore interface has two public implementations; A JFace PreferenceStore based on *.properties files and an Eclipse 3.x ScopedPreferenceStore based on *.prefs files. Consumers who want to use scoped preferences therefore always carry a direct dependency to the 3.x framework, rendering this interface unusable in a pure 4.x application.

Consumers currently have to fall back to the IEclipsePreferences interface, which is not a drop-in replacement, however, due to a different behavior when it comes to e.g. default values.

Because the ScopedPreferenceStore doesn't depend on any 3.x-specific functionality, it can be easily moved to the JFace component. The dependency to the Eclipse Runtime bundle can be reduced to the Equinox Preferences bundle by inlining the call to IPreferenceService.get.

Note that this change has to be split up into two commits; One where the ScopedPreferenceStore is moved and one where a dummy implementation is added to the Eclipse Workbench bundle. Otherwise Git is unable to detect the class as being moved to the JFace component and thus doesn't carry over the file history.

Contributes to #4262

@ptziegler

Copy link
Copy Markdown
Contributor Author

@vogella

@eclipse-platform-bot

eclipse-platform-bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

This pull request changes some projects for the first time in this development cycle.
Therefore the following files need a version increment:

bundles/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF

An additional commit containing all the necessary changes was pushed to the top of this PR's branch. To obtain these changes (for example if you want to push more changes) either fetch from your fork or apply the git patch.

Git patch
From bfc6e642e3754fb02385d823e5ea47e4043679b8 Mon Sep 17 00:00:00 2001
From: Eclipse Platform Bot <platform-bot@eclipse.org>
Date: Mon, 24 Aug 2026 19:21:50 +0000
Subject: [PATCH] Version bump(s) for 4.41 stream


diff --git a/bundles/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
index 00c668e18e..1c91dd1514 100644
--- a/bundles/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.ui.genericeditor/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.ui.genericeditor;singleton:=true
-Bundle-Version: 1.4.100.qualifier
+Bundle-Version: 1.4.200.qualifier
 Bundle-Vendor: %Bundle-Vendor
 Bundle-RequiredExecutionEnvironment: JavaSE-21
 Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0",
-- 
2.55.0

Further information are available in Common Build Issues - Missing version increments.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves scoped preference storage into JFace, reducing reliance on the Eclipse 3.x workbench.

Changes:

  • Adds the JFace ScopedPreferenceStore and Equinox Preferences dependency.
  • Retains a compatibility wrapper and migrates internal consumers.
  • Moves and modernizes the associated tests.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
PreferencesTestSuite.java Removes relocated test.
ScopedPreferenceStoreTest.java Moves test to JFace and JUnit 5.
AllPrefsTests.java Registers relocated test.
Workbench ScopedPreferenceStore.java Adds compatibility wrapper.
AbstractUIPlugin.java Uses JFace implementation.
PlatformUI.java Uses JFace implementation.
WorkbenchMessages.java Removes migrated message field.
Workbench messages.properties Removes migrated message.
ViewsPreferencePage.java Migrates preference-store import.
WorkbenchChainedTextFontFieldEditor.java Migrates preference-store import.
IDEWorkspacePreferencePage.java Migrates preference-store import.
ChooseWorkspaceData.java Migrates preference-store import.
IDEApplication.java Migrates preference-store import.
GenericEditorPreferencePage.java Migrates preference-store type checks.
Generic Editor MANIFEST.MF Increments bundle version.
TextEditorPreferencePage.java Migrates preference-store import.
JFace ScopedPreferenceStore.java Adds scoped preference implementation.
JFace messages.properties Adds validation message.
JFace MANIFEST.MF Adds Equinox Preferences dependency.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Test Results

   858 files  ±0     858 suites  ±0   58m 17s ⏱️ +33s
 8 174 tests ±0   7 931 ✅ ±0  243 💤 ±0  0 ❌ ±0 
20 427 runs  +3  19 771 ✅ +3  656 💤 ±0  0 ❌ ±0 

Results for commit af680a9. ± Comparison against base commit f0ef795.

This pull request removes 2 and adds 2 tests. Note that renamed tests count towards both.
org.eclipse.ui.tests.preferences.ScopedPreferenceStoreTestCase ‑ testNeedsSaving
org.eclipse.ui.tests.preferences.ScopedPreferenceStoreTestCase ‑ testRestoreDefaults
org.eclipse.jface.tests.preferences.ScopedPreferenceStoreTest ‑ testNeedsSaving
org.eclipse.jface.tests.preferences.ScopedPreferenceStoreTest ‑ testRestoreDefaults

♻️ This comment has been updated with latest results.

@vogella

vogella commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Thanks, @ptziegler

I would leave 'AbstractUIPlugin.getPreferenceStore()' and 'PlatformUI.getPreferenceStore()' returning the old type for now to avoid class cast exceptions in client code which are casting to it.

I also suggest to Deprecate the old type and mark it for deletion. After the deprecation has been around for at least a milestone we could IMHO change 'AbstractUIPlugin.getPreferenceStore()' and 'PlatformUI.getPreferenceStore()' but I would not do this immediately without warning to consumer.

@laeubi laeubi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would fetching the IPreferencesService through org.eclipse.core.internal.preferences.PreferencesService.getDefault() - this is actually internal API but I think in this case acceptable (and we already have other references in platform) and we likely never will change this anyways and would prevent us to duplicate the implementation. Alternatively we could add a method IPreferencesService.get() at Equinox side.

As a nit-pick, the IPreferenceStore javadoc should be updated as it currently mentions only PreferenceStore as the only implementation.

import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IDialogSettingsProvider;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.ScopedPreferenceStore;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also retain the old implementation here.

Instead we should add a static method ScopedPreferenceStore#instance(String id) that return new ScopedPreferenceStore(InstanceScope.INSTANCE, id); and mention it in the javadoc of getPreferenceStore as an alternative for consumer not wanting to use an activator.

To make it even more convenient we could additionally add ScopedPreferenceStore#instance(Class<?> context) and use FrameworkUtil#getBundle to a story from the bundle ID itself (the most common case).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would fetching the IPreferencesService through org.eclipse.core.internal.preferences.PreferencesService.getDefault()

I like that idea and have updated the PR.

To make it even more convenient we could additionally add ScopedPreferenceStore#instance(Class<?> context) and use FrameworkUtil#getBundle to a story from the bundle ID itself (the most common case).

I don't want to overdo the static methods so I think the instance(Class<?>) method is sufficient. Personally I think that using a raw string is bad practice, because you won't get any notice if the bundle is ever renamed or just outright deleted.

For the name, I've decided to use getInstance(...), because that's what's used in other JFace classes as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I think that using a raw string is bad practice, because you won't get any notice if the bundle is ever renamed or just outright deleted.

Strings become handy if you do not want to bind directly to the bundle (e.g. it is not your own), but of course this is optional and can be added any time later if we find it becomes necessary.

org.eclipse.equinox.common;bundle-version="[3.18.0,4.0.0)",
org.eclipse.equinox.bidi;bundle-version="[0.10.0,2.0.0)";resolution:=optional
org.eclipse.equinox.bidi;bundle-version="[0.10.0,2.0.0)";resolution:=optional,
org.eclipse.equinox.preferences;bundle-version="[3.12.100,4.0.0)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As ScopedPreferenceStore is the only consumer we can make this optional, even though I generally try to avoid this, it would not introduce any new dependency.

If preferences does not contain a split package, an import-package might be more suitable here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost all problems I've ever encountered with OSGi can be traced back to some weird Import-Package shenanigans. So I'd like to avoid that, if possible. Marking this dependency as optional makes sense though, especially because the same is also done for the bidi bundle, which is referenced in the BidiUtils class.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost all problems I've ever encountered with OSGi can be traced back to some weird Import-Package shenanigans.

Import-Package is the OSGi preferred way, just because people use it wrong do not mean it is flawed but Require-Bundle has some serve limitations see:

I also think it is the responsibility of eclipse-platform to set good examples and "avoiding" something because of vague feelings seems more than wrong. So if in this concrete case there is no explicit reason to use Require-Bundle (e.g. a split package) we should import the API with a proper version range as a package.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import-Package is the OSGi preferred way

That doesn't mean it's a good way... I think it's the third or fourth time by now where a customer reported a bug of the form: "The application gets stuck during the splash screen, the CPU spins up to 100% and the program gobbles up all available memory", and that just within a few years. And every time, the culprit was an erroneous Import-Package header that caused the Equinox resolver to completely go out of control.

just because people use it wrong do not mean it is flawed

Isn't simply the fact that so many people get it wrong an indicator, that this part of the specification is just not very good?

I also think it is the responsibility of eclipse-platform to set good examples

The Eclipse platform should use whatever is the least likely to break applications. And Import-Package headers are right up there.

because of vague feelings

What about years of experience with working on an imperfect application that depends on imperfect bundles? The vast majority of bundles I've seen don't have a clean manifest and as such continue to cause problems. In a perfect world, Import-Packages might be superior. But in reality, I have yet to see this being the case.

@vogella vogella Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have bad experience with import packages. Lots of my clients have to use -Dequinox.resolver.revision.batch.size=1 to run their unit tests, without that the resolver goes crazy in the import package resolver, IIRC this was analysed a few years ago in Equinox but we did not find a solution for that.

@laeubi laeubi Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC this was analysed a few years ago in Equinox but we did not find a solution for that.

This has nothing to do with import-package (and require bundle makes it even more worse especially bogus re-exports) but more with package-substitution (and equinox.resolver.revision.batch.size=1 just tells the resolver not to try finding a valid solution and leave it to the user to resolve conflicts).

I analyzed this and worked on it already on this and its solvable ... but people better complain and using "hacks" and have bad feelings so no one very yet cared to pay me for fixing that part more deeply.

In any case this is not a problem here, so one should not transfer bad feelings from other areas to unrelated things, apart from that import-package support in PDE and platform was always improved over time.

And every time, the culprit was an erroneous Import-Package header that caused the Equinox resolver to completely go out of control.

That does not mean a proper Import-Package does cause any problems, that really sounds a bit like "because others do it wrong we can't do it right" ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would seem to me better to use a package import. The package is versioned and is not a split package, so this seems like a best practice where it's generally good that the platform demonstrate best practices..

But let's not start a religious war on the topic. Others may feel differently for reasons that may seem misguided. In my experience resolution problems come, as @laeubi suggests, more from bundles the import their (locally provided) exported packages and do so with range constraint that is too permissive (i.e., plain wrong). That might lead one to conclude that package imports are bad when in fact importing your exports is something to be done only if there is a very good reason. When in doubt don't do it. But I think it's the default to do that, so Orbit generally does this to avoid that behavior:

Export-Package:         *;version="${version}";-noimport:=true

…4262

The `IPreferenceStore` interface has two public implementations; A JFace
`PreferenceStore` based on _*.properties_ files and an Eclipse 3.x
`ScopedPreferenceStore` based on _*.prefs_ files. Consumers who want to
use scoped preferences therefore always carry a direct dependency to the
3.x framework, rendering this interface unusable in a pure 4.x
application.

Consumers currently have to fall back to the `IEclipsePreferences`
interface, which is not a drop-in replacement, however, due to a
different behavior when it comes to e.g. default values.

Because the `ScopedPreferenceStore` doesn't depend on any 3.x-specific
functionality, it can be easily moved to the JFace component. The
dependency to the Eclipse Runtime bundle can be reduced to the Equinox
Preferences bundle by inlining the call to `IPreferenceService.get`.

Note that this change has to be split up into two commits; One where the
`ScopedPreferenceStore` is moved and one where a dummy implementation is
added to the Eclipse Workbench bundle. Otherwise Git is unable to detect
the class as being moved to the JFace component and thus doesn't carry
over the file history.

Contributes to
eclipse-platform#4262
…ipse-platform#4262

This adds the `ScopedPreferenceStore` back to the Platform Workbench
bundle to restore backwards compatibility. Note that this is done in a
separate commit, to make sure that the history of this class now resides
with the preference store that has been moved to the JFace component.

The old implementation has been deprecated for removal, but is still
being used in the `PlatformUI` and `AbstractUIPlugin` for backwards
compatibility, in case of a type cast on the returned store.

Closes eclipse-platform#4262
@ptziegler

Copy link
Copy Markdown
Contributor Author

I've also marked the "old" ScopedPreferenceStore as "for removal" and supressed the warnings in the PluginUI and AbstractUIPlugin classes, as suggested by @vogella. A quick search indicated that they are still used in org.eclipse.ui.console and org.eclipse.jdt.ui. So I'll have to create PRs for those projects as well.

@laeubi

laeubi commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

I've also marked the "old" ScopedPreferenceStore as "for removal" and supressed the warnings in the PluginUI and AbstractUIPlugin classes, as suggested by @vogella.

I must confess I'm not convinced we should deprecate any of this API - it does not bring any value or introduce any maintenance risk here but is massively used across the eco-system (as there was no real alternative anyways in the past) and removing it will likely break thousand of plugins for no benefit.

@merks

merks commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

The behavior or deprecating and removing sometimes feels similar to death by 1,000 paper cuts. While the Platform may have an very active vibrant community, the vast majority of the ecosystem is in maintenance mode. Moreover, users who find that some cool bundle they use every day suddenly stops working will just blame "Eclipse". They won't be impressed by how wonderfully clean code base nor by how much better e4, which they aren't using, works for whomever is using that. So it's great to make things better, but maybe we can do that without making 1,000 people make changes just to keep it working as well as before, with no actual functional benefit to be gained for their use case.

@ptziegler

Copy link
Copy Markdown
Contributor Author

I don't have any strong opinion on marking it as "for removal" or not. But I would still like to deprecate it so that it isn't used in new code.

@merks

merks commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Deprecation is merely annoying. But then most projects ignore the 10,000 warnings in their projects (including the Platform's for its projects). The sea of yellow is of little concern. Clients are familiar with being annoyed. Many are more focused on running on a wide range of dependencies than on making their frameworks work only on the latest version. That is to the benefit of the end-users and their consumers...

Removal is catastrophic for the consumers who really don't spend every release cycle looking for new warnings that they can't spot among the 10,000 they are currently ignoring. It's even worse for the users who might well choose something other than Eclipse.

@ptziegler

Copy link
Copy Markdown
Contributor Author

Yes, a deprecation is annoying. But just because something is deprecated doesn't mean it's going to be removed and as such wouldn't break client code. And to me that's an important distinction.

At the very least the documentation of the "UI" ScopedPreferenceStore should mention that the "JFace" ScopedPreferenceStore exists as a drop-in replacement...

@merks

merks commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Yes, deprecation is merely annoying, but, as you suggest, folks can live with annoying. And the user users won't notice at all. It's not unreasonable to encourage the use of new better ways, especially for new code. But we also should be realistic at this point in Eclipse's life that the volume of old code far outweighs volume of new code that is likely to be created. So how much annoyance do we wish to create for the existing clients in favor of the benefits for hose who will actually benefit? It's a tradeoff and of course as the provider of a framework we want it to be pure, shinny, and perfect. But that is not without its downside. Removal basically has mostly only downsides.


For example, we might consider to deprecate the Path constructor because there is a better way:

image

But then I have to spend quite some time dealing with that:

image

EMF builds with very old target platforms that long predate the new and improved way so my code won't get any prettier as a result of the shiny jewel at the center of the ecosystem....

@laeubi

laeubi commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

I don't have any strong opinion on marking it as "for removal" or not. But I would still like to deprecate it so that it isn't used in new code.

I think its fair to deprecate the class itself (as one should use the interface in client code anyways), but I don't see why deprecating the method helps in any ways - a plugin using AbstractUIPlugin won't benefit in migration in any kind unless the plugin plans to move away from E3 entirely and then it's good to have a javadoc note there to inform about alternatives.

The problem with deprecation is more that it often describes that it is deprecated but not why and what are the alternatives. Also sooner or later then one will argue "it has be deprecated for so long" and mark it for removal again.

So I think any deprecation should be choose with whats the benefits for the user/developer and explicitly state this - in wich case there is a noteable benefit users might start to migrate anyways.

@ptziegler

Copy link
Copy Markdown
Contributor Author

I think its fair to deprecate the class itself (as one should use the interface in client code anyways), but I don't see why deprecating the method helps in any ways - a plugin using AbstractUIPlugin won't benefit in migration in any kind unless the plugin plans to move away from E3 entirely and then it's good to have a javadoc note there to inform about alternatives.

Which is exactly what I've done. And I wouldn't want to deprecate the utility methods for exactly this reason. I only suppress the deprecation warnings in those methods, because they still use the "UI" store.

At least for the time being, I'll probably remove this annotation again, as this discussion is not relevant for the goal of this PR, which is to have a scoped preference store that doesn't add a dependency to an Eclipse 3.x plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants