Project

General

Profile

Download (9.96 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2014 OPCoach.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     OPCoach - initial API and implementation
10
 *******************************************************************************/
11
package com.opcoach.e4.preferences;
12

    
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.Collections;
16
import java.util.HashMap;
17
import java.util.Map;
18

    
19
import javax.inject.Inject;
20

    
21
import org.eclipse.core.runtime.IConfigurationElement;
22
import org.eclipse.core.runtime.IExtensionRegistry;
23
import org.eclipse.core.runtime.preferences.InstanceScope;
24
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
25
import org.eclipse.e4.core.contexts.IEclipseContext;
26
import org.eclipse.e4.core.di.annotations.Creatable;
27
import org.eclipse.e4.core.services.contributions.IContributionFactory;
28
import org.eclipse.e4.core.services.log.Logger;
29
import org.eclipse.jface.preference.IPreferenceNode;
30
import org.eclipse.jface.preference.IPreferenceStore;
31
import org.eclipse.jface.preference.PreferenceManager;
32
import org.eclipse.jface.preference.PreferenceNode;
33
import org.eclipse.jface.preference.PreferencePage;
34
import org.eclipse.swt.SWT;
35
import org.eclipse.swt.widgets.Composite;
36
import org.eclipse.swt.widgets.Control;
37
import org.eclipse.swt.widgets.Label;
38

    
39
@Creatable
40
public class E4PreferenceRegistry
41
{
42

    
43
	public static final String PREFS_PAGE_XP = "eu.etaxonomy.taxeditor.workbench.e4PreferencePages"; // $NON-NLS-1$
44
	public static final String PREF_STORE_PROVIDER = "eu.etaxonomy.taxeditor.workbench.e4PreferenceStoreProvider"; // $NON-NLS-1$
45
	protected static final String ELMT_PAGE = "page"; // $NON-NLS-1$
46
	protected static final String ATTR_ID = "id"; // $NON-NLS-1$
47
	protected static final String ATTR_CATEGORY = "category"; // $NON-NLS-1$
48
	protected static final String ATTR_CLASS = "class"; // $NON-NLS-1$
49
	protected static final String ATTR_NAME = "name"; // $NON-NLS-1$
50

    
51
	protected static final String ATTR_PLUGIN_ID = "pluginId"; // $NON-NLS-1$
52
	protected static final String ATTR_ID_IN_WBCONTEXT = "idInWorkbenchContext"; // $NON-NLS-1$
53

    
54
	@Inject
55
	protected Logger logger;
56

    
57
	@Inject
58
	protected IEclipseContext context;
59

    
60
	@Inject
61
	protected IExtensionRegistry registry;
62

    
63
	private PreferenceManager pm = null;
64

    
65
	// A map of (pluginId, { IPreferenceStoreProvider, or key in wbcontext }
66
	private Map<String, Object> psProviders;
67

    
68
	public PreferenceManager getPreferenceManager()
69
	{
70

    
71
		// Remember of the unbounded nodes to order parent pages.
72
		// Map<category, list of children> (all nodes except root nodes)
73
		Map<String, Collection<IPreferenceNode>> childrenNodes = new HashMap<String, Collection<IPreferenceNode>>();
74

    
75
		if (pm != null) {
76
            return pm;
77
        }
78

    
79
		pm = new PreferenceManager();
80
		IContributionFactory factory = context.get(IContributionFactory.class);
81

    
82
		for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREFS_PAGE_XP))
83
		{
84
			String bundleId = elmt.getNamespaceIdentifier();
85
			if (!elmt.getName().equals(ELMT_PAGE))
86
			{
87
				logger.warn("unexpected element: {0}", elmt.getName());
88
				continue;
89
			} else if (isEmpty(elmt.getAttribute(ATTR_ID)) || isEmpty(elmt.getAttribute(ATTR_NAME)))
90
			{
91
				logger.warn("missing id and/or name: {}", bundleId);
92
				continue;
93
			}
94
			PreferenceNode pn = null;
95
			if (elmt.getAttribute(ATTR_CLASS) != null)
96
			{
97
				PreferencePage page = null;
98
				try
99
				{
100
					String prefPageURI = getClassURI(bundleId, elmt.getAttribute(ATTR_CLASS));
101
					Object object = factory.create(prefPageURI, context);
102
					if (!(object instanceof PreferencePage))
103
					{
104
						logger.error("Expected instance of PreferencePage: {0}", elmt.getAttribute(ATTR_CLASS));
105
						continue;
106
					}
107
					page = (PreferencePage) object;
108
					setPreferenceStore(bundleId, page);
109

    
110
				} catch (ClassNotFoundException e)
111
				{
112
					logger.error(e);
113
					continue;
114
				}
115
				ContextInjectionFactory.inject(page, context);
116
				if ((page.getTitle() == null || page.getTitle().isEmpty()) && elmt.getAttribute(ATTR_NAME) != null)
117
				{
118
					page.setTitle(elmt.getAttribute(ATTR_NAME));
119
				}
120

    
121
				pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), page);
122
			} else
123
			{
124
				pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), new EmptyPreferencePage(elmt.getAttribute(ATTR_NAME)));
125
			}
126

    
127
			// Issue 2 : Fix bug on order (see :
128
			// https://github.com/opcoach/e4Preferences/issues/2)
129
			// Add only pages at root level and remember of child pages for
130
			// categories
131
			String category = elmt.getAttribute(ATTR_CATEGORY);
132
			if (isEmpty(category))
133
			{
134
				pm.addToRoot(pn);
135
			} else
136
			{
137
				/*
138
				 * IPreferenceNode parent = findNode(pm, category); if (parent
139
				 * == null) { // No parent found, but may be the extension has
140
				 * not been read yet. So remember of it unboundedNodes.put(pn,
141
				 * category); } else { parent.add(pn); }
142
				 */
143
				// Check if this category is already registered.
144
				Collection<IPreferenceNode> children = childrenNodes.get(category);
145
				if (children == null)
146
				{
147
					children = new ArrayList<IPreferenceNode>();
148
					childrenNodes.put(category, children);
149
				}
150
				children.add(pn);
151
			}
152
		}
153

    
154
		// Must now bind pages that has not been added in nodes (depends on the
155
		// preference page read order)
156
		// Iterate on all possible categories
157
		Collection<String> categoriesDone = new ArrayList<String>();
158

    
159
		while (!childrenNodes.isEmpty())
160
		{
161
			for (String cat : Collections.unmodifiableSet(childrenNodes.keySet()))
162
			{
163
				// Is this category already in preference manager ? If not add
164
				// it later...
165
				IPreferenceNode parent = findNode(pm, cat);
166
				if (parent != null)
167
				{
168
					// Can add the list of children to this parent page...
169
					for (IPreferenceNode pn : childrenNodes.get(cat))
170
					{
171
						parent.add(pn);
172
					}
173
					// Ok This parent page is done. Can remove it from map
174
					// outside of this loop
175
					categoriesDone.add(cat);
176
				}
177
			}
178

    
179
			for (String keyToRemove : categoriesDone) {
180
                childrenNodes.remove(keyToRemove);
181
            }
182
			categoriesDone.clear();
183

    
184
		}
185

    
186
		return pm;
187
	}
188

    
189
	private void setPreferenceStore(String bundleId, PreferencePage page)
190
	{
191
		// Affect preference store to this page if this is a
192
		// PreferencePage, else, must manage it internally
193
		// Set the issue#1 on github :
194
		// https://github.com/opcoach/e4Preferences/issues/1
195
		// And manage the extensions of IP
196
		initialisePreferenceStoreProviders();
197

    
198
		IPreferenceStore store = null;
199

    
200
		// Get the preference store according to policy.
201
		Object data = psProviders.get(bundleId);
202
		if (data != null)
203
		{
204
			if (data instanceof IPreferenceStore) {
205
                store = (IPreferenceStore) data;
206
            } else if (data instanceof IPreferenceStoreProvider) {
207
                store = ((IPreferenceStoreProvider) data).getPreferenceStore();
208
            } else if (data instanceof String) {
209
                store = (IPreferenceStore) context.get((String) data);
210
            }
211

    
212
		} else
213
		{
214
			// Default behavior : create a preference store for this bundle and remember of it
215
			store = new ScopedPreferenceStore(InstanceScope.INSTANCE, bundleId);
216
			psProviders.put(bundleId, store);
217
		}
218

    
219

    
220
		if (store != null) {
221
            page.setPreferenceStore(store);
222
        } else
223
		{
224
			logger.warn("Unable to set the preferenceStore for page " + page.getTitle() + " defined in bundle " + bundleId);
225
		}
226

    
227
	}
228

    
229
	/** Read the e4PreferenceStoreProvider extension point */
230
	private void initialisePreferenceStoreProviders()
231
	{
232
		if (psProviders == null)
233
		{
234
			IContributionFactory factory = context.get(IContributionFactory.class);
235

    
236
			psProviders = new HashMap<String, Object>();
237

    
238
			// Read extensions and fill the map...
239
			for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREF_STORE_PROVIDER))
240
			{
241
				String declaringBundle = elmt.getNamespaceIdentifier();
242
				String pluginId = elmt.getAttribute(ATTR_PLUGIN_ID);
243
				if (isEmpty(pluginId))
244
				{
245
					logger.warn("missing plugin Id in extension " + PREF_STORE_PROVIDER + " check the plugin " + declaringBundle);
246
					continue;
247
				}
248

    
249
				String classname = elmt.getAttribute(ATTR_CLASS);
250
				String objectId = elmt.getAttribute(ATTR_ID_IN_WBCONTEXT);
251

    
252
				if ((isEmpty(classname) && isEmpty(objectId)) || (((classname != null) && classname.length() > 0) && ((objectId != null) && objectId.length() > 0)))
253
				{
254
					logger.warn("In extension " + PREF_STORE_PROVIDER + " only one of the two attributes (pluginId or idInWorkbenchContext) must be set. Check the plugin "
255
							+ declaringBundle);
256
					continue;
257
				}
258

    
259
				// Ok can now work with data...
260
				Object data = objectId;
261
				if (classname != null)
262
				{
263
					data = factory.create(classname, context);
264
					if (!(data instanceof IPreferenceStoreProvider))
265
					{
266
						logger.warn("In extension " + PREF_STORE_PROVIDER + " the class must implements IPreferenceStoreProvider. Check the plugin " + declaringBundle);
267
						continue;
268
					}
269
				}
270

    
271
				psProviders.put(pluginId, data);
272

    
273
			}
274
		}
275
	}
276

    
277
	private IPreferenceNode findNode(PreferenceManager pm, String categoryId)
278
	{
279
		for (Object o : pm.getElements(PreferenceManager.POST_ORDER))
280
		{
281
			if (o instanceof IPreferenceNode && ((IPreferenceNode) o).getId().equals(categoryId))
282
			{
283
				return (IPreferenceNode) o;
284
			}
285
		}
286
		return null;
287
	}
288

    
289
	private String getClassURI(String definingBundleId, String spec) throws ClassNotFoundException
290
	{
291
		if (spec.startsWith("platform:"))
292
		{
293
			return spec;
294
		} // $NON-NLS-1$
295
		return "bundleclass://" + definingBundleId + '/' + spec;
296
	}
297

    
298
	private boolean isEmpty(String value)
299
	{
300
		return value == null || value.trim().isEmpty();
301
	}
302

    
303
	static class EmptyPreferencePage extends PreferencePage
304
	{
305

    
306
		public EmptyPreferencePage(String title)
307
		{
308
			setTitle(title);
309
			noDefaultAndApplyButton();
310
		}
311

    
312
		@Override
313
		protected Control createContents(Composite parent)
314
		{
315
			return new Label(parent, SWT.NONE);
316
		}
317

    
318
	}
319

    
320
}
(1-1/3)