package org.goko.common.preferences.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Creatable;
import org.eclipse.e4.core.services.contributions.IContributionFactory;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.goko.common.preferences.IPreferenceStoreProvider;
import org.goko.common.preferences.ScopedPreferenceStore;
import org.goko.core.config.GokoPreference;
@Creatable
public class E4PreferenceRegistry{
/* */
//public static final String PREFS_PAGE = "Goko.org.goko.ui.gkPreferencesPages";
public static final String PAGE_ID = "goko.org.ui.page.id";
/* */
public static final String PREFS_PAGE_XP = "Goko.org.goko.ui.gkPreferencePage"; // $NON-NLS-1$
public static final String PREF_STORE_PROVIDER = "Goko.org.goko.ui.gkPreferenceStoreProvider"; // $NON-NLS-1$
protected static final String ELMT_PAGE = "page"; // $NON-NLS-1$
protected static final String ATTR_ID = "id"; // $NON-NLS-1$
protected static final String ATTR_CATEGORY = "category"; // $NON-NLS-1$
protected static final String ATTR_CLASS = "class"; // $NON-NLS-1$
protected static final String ATTR_NAME = "name"; // $NON-NLS-1$
protected static final String ATTR_TARGET_BOARD = "targetBoard"; // $NON-NLS-1$
protected static final String ATTR_PLUGIN_ID = "pluginId"; // $NON-NLS-1$
protected static final String ATTR_ID_IN_WBCONTEXT = "idInWorkbenchContext"; // $NON-NLS-1$
@Inject
protected Logger logger;
@Inject
protected IEclipseContext context;
@Inject
protected IExtensionRegistry registry;
private PreferenceManager pm = null;
// A map of (pluginId, { IPreferenceStoreProvider, or key in wbcontext }
private Map<String, Object> psProviders;
public PreferenceManager getPreferenceManager()
{
// Remember of the unbounded nodes to order parent pages.
// Map<category, list of children> (all nodes except root nodes)
Map<String, Collection<IPreferenceNode>> childrenNodes = new HashMap<String, Collection<IPreferenceNode>>();
if (pm != null)
return pm;
pm = new PreferenceManager();
IContributionFactory factory = context.get(IContributionFactory.class);
String targetBoard = GokoPreference.getInstance().getTargetBoard();
for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREFS_PAGE_XP))
{
String bundleId = elmt.getNamespaceIdentifier();
if (!elmt.getName().equals(ELMT_PAGE))
{
logger.warn("unexpected element: {0}", elmt.getName());
continue;
} else if (isEmpty(elmt.getAttribute(ATTR_ID)) || isEmpty(elmt.getAttribute(ATTR_NAME)))
{
logger.warn("missing id and/or name: {}", bundleId);
continue;
}
String pageTargetBoard = elmt.getAttribute(ATTR_TARGET_BOARD);
if(StringUtils.isNotBlank(pageTargetBoard) && !StringUtils.equals(pageTargetBoard, targetBoard)){
continue; // Page is not designed for target board. Ignore it
}
PreferenceNode pn = null;
if (elmt.getAttribute(ATTR_CLASS) != null)
{
PreferencePage page = null;
try
{
String prefPageURI = getClassURI(bundleId, elmt.getAttribute(ATTR_CLASS));
Object object = factory.create(prefPageURI, context);
if (!(object instanceof PreferencePage))
{
logger.error("Expected instance of PreferencePage: {0}", elmt.getAttribute(ATTR_CLASS));
continue;
}
page = (PreferencePage) object;
setPreferenceStore(bundleId, page);
} catch (ClassNotFoundException e)
{
logger.error(e);
continue;
}
ContextInjectionFactory.inject(page, context);
if ((page.getTitle() == null || page.getTitle().isEmpty()) && elmt.getAttribute(ATTR_NAME) != null)
{
page.setTitle(elmt.getAttribute(ATTR_NAME));
}
pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), page);
} else
{
pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), new EmptyPreferencePage(elmt.getAttribute(ATTR_NAME)));
}
// Issue 2 : Fix bug on order (see :
// https://github.com/opcoach/e4Preferences/issues/2)
// Add only pages at root level and remember of child pages for
// categories
String category = elmt.getAttribute(ATTR_CATEGORY);
if (isEmpty(category))
{
pm.addToRoot(pn);
} else
{
/*
* IPreferenceNode parent = findNode(pm, category); if (parent
* == null) { // No parent found, but may be the extension has
* not been read yet. So remember of it unboundedNodes.put(pn,
* category); } else { parent.add(pn); }
*/
// Check if this category is already registered.
Collection<IPreferenceNode> children = childrenNodes.get(category);
if (children == null)
{
children = new ArrayList<IPreferenceNode>();
childrenNodes.put(category, children);
}
children.add(pn);
}
}
// Must now bind pages that has not been added in nodes (depends on the
// preference page read order)
// Iterate on all possible categories
Collection<String> categoriesDone = new ArrayList<String>();
while (!childrenNodes.isEmpty())
{
for (String cat : Collections.unmodifiableSet(childrenNodes.keySet()))
{
// Is this category already in preference manager ? If not add
// it later...
IPreferenceNode parent = findNode(pm, cat);
if (parent != null)
{
// Can add the list of children to this parent page...
for (IPreferenceNode pn : childrenNodes.get(cat))
{
parent.add(pn);
}
// Ok This parent page is done. Can remove it from map
// outside of this loop
categoriesDone.add(cat);
}
}
for (String keyToRemove : categoriesDone)
childrenNodes.remove(keyToRemove);
categoriesDone.clear();
}
return pm;
}
private void setPreferenceStore(String bundleId, PreferencePage page)
{
// Affect preference store to this page if this is a
// PreferencePage, else, must manage it internally
// Set the issue#1 on github :
// https://github.com/opcoach/e4Preferences/issues/1
// And manage the extensions of IP
initialisePreferenceStoreProviders();
IPreferenceStore store = null;
// Get the preference store according to policy.
Object data = psProviders.get(bundleId);
if (data != null)
{
if (data instanceof IPreferenceStore)
store = (IPreferenceStore) data;
else if (data instanceof IPreferenceStoreProvider)
store = ((IPreferenceStoreProvider) data).getPreferenceStore();
else if (data instanceof String)
store = (IPreferenceStore) context.get((String) data);
} else
{
// Default behavior : create a preference store for this bundle and remember of it
store = new ScopedPreferenceStore(InstanceScope.INSTANCE, bundleId);
psProviders.put(bundleId, store);
}
if (store != null)
page.setPreferenceStore(store);
else
{
logger.warn("Unable to set the preferenceStore for page " + page.getTitle() + " defined in bundle " + bundleId);
}
}
/** Read the e4PreferenceStoreProvider extension point */
private void initialisePreferenceStoreProviders()
{
if (psProviders == null)
{
IContributionFactory factory = context.get(IContributionFactory.class);
psProviders = new HashMap<String, Object>();
// Read extensions and fill the map...
for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREF_STORE_PROVIDER))
{
String declaringBundle = elmt.getNamespaceIdentifier();
//String pluginId = elmt.getAttribute(ATTR_PLUGIN_ID);
// if (isEmpty(pluginId))
// {
// logger.warn("missing plugin Id in extension " + PREF_STORE_PROVIDER + " check the plugin " + declaringBundle);
// continue;
// }
if(psProviders.containsKey(declaringBundle)){
logger.error("Multiple preference store for a single bundle is not supported yet");
}
String classname = elmt.getAttribute(ATTR_CLASS);
String objectId = elmt.getAttribute(ATTR_ID_IN_WBCONTEXT);
if ((isEmpty(classname) && isEmpty(objectId)) || (((classname != null) && classname.length() > 0) && ((objectId != null) && objectId.length() > 0)))
{
logger.warn("In extension " + PREF_STORE_PROVIDER + " only one of the two attributes (pluginId or idInWorkbenchContext) must be set. Check the plugin "
+ declaringBundle);
continue;
}
// Ok can now work with data...
Object data = objectId;
if (classname != null)
{
try{
String classURI = getClassURI(declaringBundle, elmt.getAttribute(ATTR_CLASS));
data = factory.create(classURI, context);
if (!(data instanceof IPreferenceStoreProvider))
{
logger.warn("In extension " + PREF_STORE_PROVIDER + " the class must implements IPreferenceStoreProvider. Check the plugin " + declaringBundle);
continue;
}
}catch(ClassNotFoundException e){
logger.error(e);
}
}
psProviders.put(declaringBundle, data);
}
}
}
private IPreferenceNode findNode(PreferenceManager pm, String categoryId)
{
for (Object o : pm.getElements(PreferenceManager.POST_ORDER))
{
if (o instanceof IPreferenceNode && ((IPreferenceNode) o).getId().equals(categoryId))
{
return (IPreferenceNode) o;
}
}
return null;
}
private String getClassURI(String definingBundleId, String spec) throws ClassNotFoundException
{
if (spec.startsWith("platform:"))
{
return spec;
} // $NON-NLS-1$
return "bundleclass://" + definingBundleId + '/' + spec;
}
private boolean isEmpty(String value)
{
return value == null || value.trim().isEmpty();
}
static class EmptyPreferencePage extends PreferencePage
{
public EmptyPreferencePage(String title)
{
setTitle(title);
noDefaultAndApplyButton();
}
@Override
protected Control createContents(Composite parent)
{
return new Label(parent, SWT.NONE);
}
}
}
/*
//package org.goko.common.preferences.internal;
//
//import java.util.ArrayList;
//import java.util.Collection;
//import java.util.Collections;
//import java.util.HashMap;
//import java.util.Map;
//
//import javax.inject.Inject;
//
//import org.eclipse.core.runtime.IConfigurationElement;
//import org.eclipse.core.runtime.IExtensionRegistry;
//import org.eclipse.core.runtime.preferences.InstanceScope;
//import org.eclipse.e4.core.contexts.ContextInjectionFactory;
//import org.eclipse.e4.core.contexts.IEclipseContext;
//import org.eclipse.e4.core.di.annotations.Creatable;
//import org.eclipse.e4.core.services.contributions.IContributionFactory;
//import org.eclipse.e4.core.services.log.Logger;
//import org.eclipse.jface.preference.IPreferenceNode;
//import org.eclipse.jface.preference.IPreferenceStore;
//import org.eclipse.jface.preference.PreferenceManager;
//import org.eclipse.jface.preference.PreferenceNode;
//import org.eclipse.jface.preference.PreferencePage;
//import org.eclipse.swt.SWT;
//import org.eclipse.swt.widgets.Composite;
//import org.eclipse.swt.widgets.Control;
//import org.eclipse.swt.widgets.Label;
//import org.goko.common.preferences.IPreferenceStoreProvider;
//import org.goko.common.preferences.ScopedPreferenceStore;
//
//
//@Creatable
//public class E4PreferenceRegistry{
///* */
// //public static final String PREFS_PAGE = "Goko.org.goko.ui.gkPreferencesPages";
// public static final String PAGE_ID = "goko.org.ui.page.id";
//
// /* */
// public static final String PREFS_PAGE_XP = "Goko.org.goko.ui.gkPreferencePage"; // $NON-NLS-1$
// public static final String PREF_STORE_PROVIDER = "Goko.org.goko.ui.gkPreferenceStoreProvider"; // $NON-NLS-1$
// protected static final String ELMT_PAGE = "page"; // $NON-NLS-1$
// protected static final String ATTR_ID = "id"; // $NON-NLS-1$
// protected static final String ATTR_CATEGORY = "category"; // $NON-NLS-1$
// protected static final String ATTR_CLASS = "class"; // $NON-NLS-1$
// protected static final String ATTR_NAME = "name"; // $NON-NLS-1$
//
// protected static final String ATTR_PLUGIN_ID = "pluginId"; // $NON-NLS-1$
// protected static final String ATTR_ID_IN_WBCONTEXT = "idInWorkbenchContext"; // $NON-NLS-1$
//
// @Inject
// protected Logger logger;
//
// @Inject
// protected IEclipseContext context;
//
// @Inject
// protected IExtensionRegistry registry;
//
// private PreferenceManager pm = null;
//
// // A map of (pluginId, { IPreferenceStoreProvider, or key in wbcontext }
// private Map<String, Object> psProviders;
//
// public PreferenceManager getPreferenceManager()
// {
//
// // Remember of the unbounded nodes to order parent pages.
// // Map<category, list of children> (all nodes except root nodes)
// Map<String, Collection<IPreferenceNode>> childrenNodes = new HashMap<String, Collection<IPreferenceNode>>();
//
// if (pm != null)
// return pm;
//
// pm = new PreferenceManager();
// IContributionFactory factory = context.get(IContributionFactory.class);
//
// for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREFS_PAGE_XP))
// {
// String bundleId = elmt.getNamespaceIdentifier();
// if (!elmt.getName().equals(ELMT_PAGE))
// {
// logger.warn("unexpected element: {0}", elmt.getName());
// continue;
// } else if (isEmpty(elmt.getAttribute(ATTR_ID)) || isEmpty(elmt.getAttribute(ATTR_NAME)))
// {
// logger.warn("missing id and/or name: {}", bundleId);
// continue;
// }
// PreferenceNode pn = null;
// if (elmt.getAttribute(ATTR_CLASS) != null)
// {
// PreferencePage page = null;
// try
// {
// String prefPageURI = getClassURI(bundleId, elmt.getAttribute(ATTR_CLASS));
// Object object = factory.create(prefPageURI, context);
// if (!(object instanceof PreferencePage))
// {
// logger.error("Expected instance of PreferencePage: {0}", elmt.getAttribute(ATTR_CLASS));
// continue;
// }
// page = (PreferencePage) object;
// setPreferenceStore(bundleId, page);
//
// } catch (ClassNotFoundException e)
// {
// logger.error(e);
// continue;
// }
// ContextInjectionFactory.inject(page, context);
// if ((page.getTitle() == null || page.getTitle().isEmpty()) && elmt.getAttribute(ATTR_NAME) != null)
// {
// page.setTitle(elmt.getAttribute(ATTR_NAME));
// }
//
// pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), page);
// } else
// {
// pn = new PreferenceNode(elmt.getAttribute(ATTR_ID), new EmptyPreferencePage(elmt.getAttribute(ATTR_NAME)));
// }
//
// // Issue 2 : Fix bug on order (see :
// // https://github.com/opcoach/e4Preferences/issues/2)
// // Add only pages at root level and remember of child pages for
// // categories
// String category = elmt.getAttribute(ATTR_CATEGORY);
// if (isEmpty(category))
// {
// pm.addToRoot(pn);
// } else
// {
// /*
// * IPreferenceNode parent = findNode(pm, category); if (parent
// * == null) { // No parent found, but may be the extension has
// * not been read yet. So remember of it unboundedNodes.put(pn,
// * category); } else { parent.add(pn); }
// */
// // Check if this category is already registered.
// Collection<IPreferenceNode> children = childrenNodes.get(category);
// if (children == null)
// {
// children = new ArrayList<IPreferenceNode>();
// childrenNodes.put(category, children);
// }
// children.add(pn);
// }
// }
//
// // Must now bind pages that has not been added in nodes (depends on the
// // preference page read order)
// // Iterate on all possible categories
// Collection<String> categoriesDone = new ArrayList<String>();
//
// while (!childrenNodes.isEmpty())
// {
// for (String cat : Collections.unmodifiableSet(childrenNodes.keySet()))
// {
// // Is this category already in preference manager ? If not add
// // it later...
// IPreferenceNode parent = findNode(pm, cat);
// if (parent != null)
// {
// // Can add the list of children to this parent page...
// for (IPreferenceNode pn : childrenNodes.get(cat))
// {
// parent.add(pn);
// }
// // Ok This parent page is done. Can remove it from map
// // outside of this loop
// categoriesDone.add(cat);
// }
// }
//
// for (String keyToRemove : categoriesDone)
// childrenNodes.remove(keyToRemove);
// categoriesDone.clear();
//
// }
//
// return pm;
// }
//
// private void setPreferenceStore(String bundleId, PreferencePage page)
// {
// // Affect preference store to this page if this is a
// // PreferencePage, else, must manage it internally
// // Set the issue#1 on github :
// // https://github.com/opcoach/e4Preferences/issues/1
// // And manage the extensions of IP
// initialisePreferenceStoreProviders();
//
// IPreferenceStore store = null;
//
// // Get the preference store according to policy.
// Object data = psProviders.get(bundleId);
// if (data != null)
// {
// if (data instanceof IPreferenceStore)
// store = (IPreferenceStore) data;
// else if (data instanceof IPreferenceStoreProvider)
// store = ((IPreferenceStoreProvider) data).getPreferenceStore();
// else if (data instanceof String)
// store = (IPreferenceStore) context.get((String) data);
//
// } else
// {
// // Default behavior : create a preference store for this bundle and remember of it
// store = new ScopedPreferenceStore(InstanceScope.INSTANCE, bundleId);
// psProviders.put(bundleId, store);
// }
//
//
// if (store != null)
// page.setPreferenceStore(store);
// else
// {
// logger.warn("Unable to set the preferenceStore for page " + page.getTitle() + " defined in bundle " + bundleId);
// }
//
// }
//
// /** Read the e4PreferenceStoreProvider extension point */
// private void initialisePreferenceStoreProviders()
// {
// if (psProviders == null)
// {
// IContributionFactory factory = context.get(IContributionFactory.class);
//
// psProviders = new HashMap<String, Object>();
//
// // Read extensions and fill the map...
// for (IConfigurationElement elmt : registry.getConfigurationElementsFor(PREF_STORE_PROVIDER))
// {
// String declaringBundle = elmt.getNamespaceIdentifier();
// String pluginId = elmt.getAttribute(ATTR_PLUGIN_ID);
// if (isEmpty(pluginId))
// {
// logger.warn("missing plugin Id in extension " + PREF_STORE_PROVIDER + " check the plugin " + declaringBundle);
// continue;
// }
//
// String classname = elmt.getAttribute(ATTR_CLASS);
// String objectId = elmt.getAttribute(ATTR_ID_IN_WBCONTEXT);
//
// if ((isEmpty(classname) && isEmpty(objectId)) || (((classname != null) && classname.length() > 0) && ((objectId != null) && objectId.length() > 0)))
// {
// logger.warn("In extension " + PREF_STORE_PROVIDER + " only one of the two attributes (pluginId or idInWorkbenchContext) must be set. Check the plugin "
// + declaringBundle);
// continue;
// }
//
// // Ok can now work with data...
// Object data = objectId;
// if (classname != null)
// {
// data = factory.create(classname, context);
// if (!(data instanceof IPreferenceStoreProvider))
// {
// logger.warn("In extension " + PREF_STORE_PROVIDER + " the class must implements IPreferenceStoreProvider. Check the plugin " + declaringBundle);
// continue;
// }
// }
//
// psProviders.put(pluginId, data);
//
// }
// }
// }
//
// private IPreferenceNode findNode(PreferenceManager pm, String categoryId)
// {
// for (Object o : pm.getElements(PreferenceManager.POST_ORDER))
// {
// if (o instanceof IPreferenceNode && ((IPreferenceNode) o).getId().equals(categoryId))
// {
// return (IPreferenceNode) o;
// }
// }
// return null;
// }
//
// private String getClassURI(String definingBundleId, String spec) throws ClassNotFoundException
// {
// if (spec.startsWith("platform:"))
// {
// return spec;
// } // $NON-NLS-1$
// return "bundleclass://" + definingBundleId + '/' + spec;
// }
//
// private boolean isEmpty(String value)
// {
// return value == null || value.trim().isEmpty();
// }
//
// static class EmptyPreferencePage extends PreferencePage
// {
//
// public EmptyPreferencePage(String title)
// {
// setTitle(title);
// noDefaultAndApplyButton();
// }
//
// @Override
// protected Control createContents(Composite parent)
// {
// return new Label(parent, SWT.NONE);
// }
//
// }
//
//}
//