Move ScopedPreferenceStore to org.eclipse.jface #4262 - #4270
Conversation
|
This pull request changes some projects for the first time in this development cycle. 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 patchFurther information are available in Common Build Issues - Missing version increments. |
There was a problem hiding this comment.
Pull request overview
Moves scoped preference storage into JFace, reducing reliance on the Eclipse 3.x workbench.
Changes:
- Adds the JFace
ScopedPreferenceStoreand 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.
Test Results 858 files ±0 858 suites ±0 58m 17s ⏱️ +33s 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.♻️ This comment has been updated with latest results. |
|
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
left a comment
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Import-Packageis 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" ...
There was a problem hiding this comment.
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
|
I've also marked the "old" |
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. |
|
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. |
|
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. |
|
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. |
|
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... |
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 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. |
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. |


The
IPreferenceStoreinterface has two public implementations; A JFacePreferenceStorebased on *.properties files and an Eclipse 3.xScopedPreferenceStorebased 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
IEclipsePreferencesinterface, which is not a drop-in replacement, however, due to a different behavior when it comes to e.g. default values.Because the
ScopedPreferenceStoredoesn'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 toIPreferenceService.get.Note that this change has to be split up into two commits; One where the
ScopedPreferenceStoreis 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