Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 384706 - AbstractPreferencePage stores per project settings in workspace metadata
Summary: AbstractPreferencePage stores per project settings in workspace metadata
Status: CLOSED FIXED
Alias: None
Product: TMF
Classification: Modeling
Component: Xtext (show other bugs)
Version: 2.3.0   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 385291
Blocks:
  Show dependency tree
 
Reported: 2012-07-10 06:31 EDT by Aaron Digulla CLA
Modified: 2017-10-31 10:59 EDT (History)
3 users (show)

See Also:


Attachments
Implementation using project store (10.11 KB, application/octet-stream)
2012-07-14 09:59 EDT, Henrik Lindberg CLA
no flags Details
updated implementation - needs (also attached) supporting classes (10.75 KB, application/octet-stream)
2012-07-17 11:50 EDT, Henrik Lindberg CLA
no flags Details
Preference store that is "project aware" (27.80 KB, text/plain)
2012-07-17 11:51 EDT, Henrik Lindberg CLA
no flags Details
preference store access using project aware preference store (2.09 KB, text/plain)
2012-07-17 11:52 EDT, Henrik Lindberg CLA
no flags Details
AbstractPreferencePage - also fixes restore default issue (10.79 KB, application/octet-stream)
2012-07-17 12:16 EDT, Henrik Lindberg CLA
no flags Details
Abstract Preference Page update with small fix (10.79 KB, text/plain)
2012-07-17 12:30 EDT, Henrik Lindberg CLA
no flags Details
all in one patch (73.39 KB, patch)
2012-07-18 10:47 EDT, Dennis Huebner CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Aaron Digulla CLA 2012-07-10 06:31:41 EDT
If you build DSL preferences pages by extending AbstractPreferencePage, then you will get the preference setting "useProjectSettings" for free.

But the setting is stored like this (AbstractPreferencePage.java):

		useProjectSettingsButton.setSelection(Boolean.valueOf(currentProject().getPersistentProperty(
					new QualifiedName(qualifiedName(), USE_PROJECT_SETTINGS))));

which means it ends up in the workspace metadata (in a PropertyBucket located at workspace/.metadata/.plugins/org.eclipse.core.resources/.projects/${project}/.indexes/properties.index

This means that all team members must open the preferences page and click the button as well to make sure all team members use the same compile options.

This is especially problematic when the project specific settings are complicated because Eclipse won't put the project .settings into the editors when you toggle the checkbox, so to use project specific settings, I have to write an email telling all team members exactly how to configure the projects.

Please change the code to save this setting in the .settings folder of the project.
Comment 1 Aaron Digulla CLA 2012-07-10 06:52:04 EDT
On a related note, the class OptionsConfigurationBlock uses the key "is_project_specific" to achieve the same thing for compiler options.

My first thought was to merge the two keys into one but this would mean that all prefs pages of a DSL would be either project or workbench specific; there would be no way to mix global and project prefs.
Comment 2 Henrik Lindberg CLA 2012-07-14 09:59:41 EDT
Created attachment 218727 [details]
Implementation using project store

This is a replacement implementation for AbstractPreferencePage that stores the 'useProjectSpecificPreferences' as a property under .settings instead of in the workspace metadata for the project.

In my case, the qualifiedName will always be the ID of the preference page. When I lookup preferences, I check if this ID is set to true in the preferences, if not, I return the preference from the non project specific preferences.
Comment 3 Henrik Lindberg CLA 2012-07-14 10:02:10 EDT
My implementation makes use of reflection so it functions with Xtext before/after 2.3 (where a different preference store class is being used).
Comment 4 Aaron Digulla CLA 2012-07-16 04:28:24 EDT
(In reply to comment #2)
> Created attachment 218727 [details]
> Implementation using project store

Comments:

1. Just using qualifiedName() will give you this entry in the settings:

   plugin-id=true

which is confusing. You should really append USE_PROJECT_SETTINGS or use a new protected method. Otherwise, there is a danger that this little implementation detail makes the class useless for some people.

2. Why don't you merge all the exception handlers in lines 277-290 into one?

3. What exactly is the problem of the TODO in line 305?
Comment 5 Henrik Lindberg CLA 2012-07-16 07:39:27 EDT
(In reply to comment #4)

Thanks for the review Aaron.

> Comments:
> 
> 1. Just using qualifiedName() will give you this entry in the settings:
> 
>    plugin-id=true
> 
> which is confusing. You should really append USE_PROJECT_SETTINGS or use a new
> protected method. Otherwise, there is a danger that this little implementation
> detail makes the class useless for some people.
> 
Agree - a bit too magic. Will append USE_PROJECT_SETTINGS to qualified name.

> 2. Why don't you merge all the exception handlers in lines 277-290 into one?
>
Yes, that should be done.
 
> 3. What exactly is the problem of the TODO in line 305?
Don't know, it is in the original AbstractPreferencePage. While testing my impl, I have noticed what looked like odd behavior when turning project-specific settings on/off. I am continuing to test and improve my replacement impl and will post updated information.
Comment 6 Henrik Lindberg CLA 2012-07-16 08:44:35 EDT
Found that there are a number of issues related to 'use project settings' as the underlying framework does not seem to be designed for this use case. 

- value is not saved if it is the same as what is looked up
- values are not saved if they are the same as the default
- boolean values have a default/default of false

This gives surprising results; a user wants to override a true value with false, this value happens to be the same as the default/default, and is therefore not saved. Result is that the project specific setting shows up as true (as it was not overwritten).

Working on a fix.
Comment 7 Dennis Huebner CLA 2012-07-16 09:27:41 EDT
(In reply to comment #6)
> Found that there are a number of issues related to 'use project settings' as
> the underlying framework does not seem to be designed for this use case. 
> 
> - value is not saved if it is the same as what is looked up
> - values are not saved if they are the same as the default
> - boolean values have a default/default of false
> 
> This gives surprising results; a user wants to override a true value with
> false, this value happens to be the same as the default/default, and is
> therefore not saved. Result is that the project specific setting shows up as
> true (as it was not overwritten).
> 
> Working on a fix.

See also at the org.eclipse.xtext.builder.preferences.PropertyAndPreferencePage class.
Comment 8 Henrik Lindberg CLA 2012-07-16 12:00:17 EDT
(In reply to comment #7)
> See also at the org.eclipse.xtext.builder.preferences.PropertyAndPreferencePage
> class.

Looked there, did not seem to deal with the issues I found.

I managed to get one step further by modifying the search context when switching to "project property mode"  - it then saves values ok, but not if a value is the same as the default value. This makes it impossible to have a project specific value (that is the same as the default value) that overrides an "instance/workspace" setting that is different than the default. e.g. default = Blue, instance = Red, and project = Blue. When saving the project setting it is removed thus exposing the instance value "Red".

For Booleans this is made worse as the default/default is false, whereas for strings it is "".

The only solution I have come up with (so far) that will handle this correctly is to store all project specific preferences with an id that is different than the instance one. The other alternative requires a lot more work and duplicated/modified classes.

The UI related classes that deal with preferences does not seem to have been written with "project specific settings" in mind. The lower level eclipse preference nodes does not interfere, they simply do what they are told - the problems is with the combination of ScopedPreferenceStore (or the new Fixed replacement) and the field editors.

Or, have I missed something, using the framework/preference pane the wrong way?
Comment 9 Henrik Lindberg CLA 2012-07-16 12:19:08 EDT
Found a different way. The ScopedPreferenceStore (FixedScopedPreferenceStore in Xtext 2.3) can be modified to handle the case as it ultimately is responsible for writing the value to a preference node.
Comment 10 Sebastian Zarnekow CLA 2012-07-16 12:45:34 EDT
Could you please try to reproduce the observed (mis-)behavior in terms of new test cases in org.eclipse.xtext.ui.tests.preferences.PreferenceStoreAccessTest ? That would be very helpful.
Comment 11 Dennis Huebner CLA 2012-07-17 08:11:15 EDT
(In reply to comment #10)
> Could you please try to reproduce the observed (mis-)behavior in terms of new
> test cases in org.eclipse.xtext.ui.tests.preferences.PreferenceStoreAccessTest
> ? That would be very helpful.
org.eclipse.xtext.ui.tests.preferences.PreferenceStoreAccessTest

@Test
	public void testProjectScopeReadWrite() throws Exception {
		try {
			
			String qualifier = "qualifier";
			
			InstanceScope instanceScope = new InstanceScope();
			ScopedPreferenceStore instanceStore = new ScopedPreferenceStore(instanceScope, qualifier);
			instanceStore.setSearchContexts(new IScopeContext[] { instanceScope, new ConfigurationScope() });

			IProject project = IResourcesSetupUtil.createProject("test");
			ProjectScope projectScope = new ProjectScope(project);
			ScopedPreferenceStore projectStore = new ScopedPreferenceStore(projectScope, qualifier);
			projectStore
					.setSearchContexts(new IScopeContext[] { projectScope, instanceScope, new ConfigurationScope() });

			instanceStore.setValue("newInt", 100);
			projectStore.setValue("newInt", 100);
			assertEquals(100, instanceStore.getInt("newInt"));
			assertEquals(100, projectStore.getInt("newInt"));

			instanceStore.setValue("newInt", 120);
			assertEquals(120, instanceStore.getInt("newInt"));
			assertEquals(100, projectStore.getInt("newInt"));
		} finally {
			IResourcesSetupUtil.cleanWorkspace();
		}
	}
Comment 12 Henrik Lindberg CLA 2012-07-17 10:34:45 EDT
(In reply to comment #11)
> (In reply to comment #10)
> > Could you please try to reproduce the observed (mis-)behavior in terms of new
> > test cases in org.eclipse.xtext.ui.tests.preferences.PreferenceStoreAccessTest
> > ? That would be very helpful.
> org.eclipse.xtext.ui.tests.preferences.PreferenceStoreAccessTest
> 
> @Test
>     public void testProjectScopeReadWrite() throws Exception {
>         try {
> ...

Tests are also needed where:
- default "newInt" is set to 200
- instance "newInt" is set to 120
- project "newInt" is set to 200
= should read back 200

With setup as in test above, then:
- change the default to "3oo"
= should read back 200
Comment 13 Henrik Lindberg CLA 2012-07-17 11:50:45 EDT
Created attachment 218813 [details]
updated implementation - needs (also attached) supporting classes
Comment 14 Henrik Lindberg CLA 2012-07-17 11:51:44 EDT
Created attachment 218814 [details]
Preference store that is "project aware"
Comment 15 Henrik Lindberg CLA 2012-07-17 11:52:26 EDT
Created attachment 218817 [details]
preference store access using project aware preference store
Comment 16 Henrik Lindberg CLA 2012-07-17 12:08:40 EDT
I have updated my implementation - classes are attached. Compared to the standard implementation, the attached implementation does the following:

The ProjectAwareScopedPreferenceStore:
- detects when writes are made to a ProjectPreferences and then skips the optimizations; no write if value equal to read value or default value.

The AbstractPreferencePage:
- is aware of the project aware preference store (they need to be in the same package as it obtains the store preference node).
- saves the 'useProjectSettings' in the project preference store using a key obtained from an overridable method (default = qualifiedName + ".useProjectSettings"
- removes all field editor keys (+ the master key) from the project store if 'useProjectSettings' is false
- copies the read values to the project store when useProjectSettings is turned on
- refreshes values when 'Manage Workspace Settings' returns (i.e shows current values).

The PPPreferenceStoreAccess:
- simply instantiates the ProjectAwareScopedPreferenceStore instead of the "default"

I think this implementation does a decent job. There are two small thing that is not handled
- there is no listener involved when the property page is non useProjectSettings mode - the (disabled) values shown will not refresh until the next time the user opens the page (they will however refresh when user switches mode, or if user changes the values via the 'Manage Workspace Values' in the dialog). For me this is good enough. 
- When pressing 'Restore Defaults' in nonUseenableProjectSettings, the disabled fields will show the default values. The expected result is that nothing should happen. (It works as expected - i.e. values are set to the defaults (not the instance settings) if pressed in 'useProjectSettings' mode.  (I will look into this issue - it is also debatable what 'Restore Defaults' should do - turn off project settings, reset to instance, or reset to default.
Comment 17 Henrik Lindberg CLA 2012-07-17 12:16:15 EDT
Created attachment 218821 [details]
AbstractPreferencePage - also fixes restore default issue

The fix to mentioned issue with "restore defaults" was simple. The restoreButton is simply disabled when the settings are non project specific as it does not really make sense to 'restore defaults' when in this state.
Comment 18 Henrik Lindberg CLA 2012-07-17 12:30:28 EDT
Created attachment 218825 [details]
Abstract Preference Page update with small fix

Hopefully final touch - as users migrate from "useProjectSettings" stored in project metadata (workspace) to the alternative implementation the user interface will show 'useProjectSettings' as 'false' (as expected). When turning on 'projectSettings' the now attached version makes sure that existing values from project settings are honored. User just has to 'turn on project settings' and save.
Comment 19 Dennis Huebner CLA 2012-07-18 10:44:52 EDT
(In reply to comment #18)
> Created attachment 218825 [details]
> Abstract Preference Page update with small fix
> 
> Hopefully final touch - as users migrate from "useProjectSettings" stored in
> project metadata (workspace) to the alternative implementation the user
> interface will show 'useProjectSettings' as 'false' (as expected). When turning
> on 'projectSettings' the now attached version makes sure that existing values
> from project settings are honored. User just has to 'turn on project settings'
> and save.

Thanks for your contribution Henrik.
I've applied the changes locally and can verify that PreferenceStoreAccessTests are still green. Our new tests, see #c11 and 385291 are also passed.
We have to decide how far we may clean up existing code.
Comment 20 Dennis Huebner CLA 2012-07-18 10:47:39 EDT
Created attachment 218878 [details]
all in one patch
Comment 21 Dennis Huebner CLA 2015-01-19 08:24:09 EST
Project specific settings works correctly now.
Comment 22 Aaron Digulla CLA 2015-01-19 11:56:49 EST
To which version of Xtext were the patches applied?
Comment 23 Dennis Huebner CLA 2015-01-19 13:37:17 EST
Can't find any changes, just tried to verify the problem you had and found any entries in workspace/.metadata/.plugins/org.eclipse.core.resources/.projects/${project}/.indexes/properties.index

was in closing old bugs flow :p
Have to investigate it deeper and create a testcase tomorrow.
Comment 24 Aaron Digulla CLA 2015-01-20 08:11:14 EST
There is a patch attached to this issue; looking at the Git history of the file should show when it was applied.

But test cases would be nice, too :-)
Comment 25 Dennis Huebner CLA 2015-01-20 10:05:35 EST
I was testing the wrong preference page as I closed the bug :)

Most of our preference pages extends org.eclipse.xtext.ui.preferences.PropertyAndPreferencePage and they worked fine. So the issue still persist.
I've tried to apply the patches but could only migrate fixes for AbstractPreferencePage. That was the initial issue, right?
Please look into   https://git.eclipse.org/r/#/c/39939/
If some other issues are still open (read/write project specific settings?) please create a test case for that. You can use our gerrit instance too. http://wiki.eclipse.org/Xtext/Contributor_Guide
Comment 26 Dennis Huebner CLA 2015-01-21 10:40:07 EST
Fix merged in master.
Please reopen or create a new bug if there are still issues.
Comment 27 Eclipse Webmaster CLA 2017-10-31 10:48:22 EDT
Requested via bug 522520.

-M.
Comment 28 Eclipse Webmaster CLA 2017-10-31 10:59:24 EDT
Requested via bug 522520.

-M.