--- /dev/null
+package org.simantics.aeri.redmine.core.di;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.preferences.ConfigurationScope;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.e4.core.contexts.IContextFunction;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.epp.logging.aeri.core.SystemControl;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+import org.simantics.aeri.redmine.core.internal.Activator;
+import org.simantics.aeri.redmine.core.settings.RedmineAERISettings;
+import org.simantics.aeri.redmine.core.settings.RedmineAERISettingsFactory;
+import org.simantics.aeri.redmine.core.settings.RedmineAERISettingsPackage;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Context function that computes the {@link RedmineAERISettings}.
+ */
+public class RedmineAERISettingsCreationFunction implements IContextFunction {
+
+ @Override
+ public Object compute(IEclipseContext localContext, String contextKey) {
+ RedmineAERISettings settings = RedmineAERISettingsFactory.eINSTANCE.createRedmineAERISettings();
+ EClass eClass = settings.eClass();
+ ScopedPreferenceStore instanceStore = new ScopedPreferenceStore(InstanceScope.INSTANCE, Activator.BUNDLE_ID);
+ loadFromPreferences(instanceStore, settings, eClass);
+ registerPreferenceStoreChangeListener(instanceStore, settings, eClass);
+ registerSettingsChangeListener(instanceStore, settings, new HashSet<EAttribute>());
+
+ // register a listener that sends selected changes to the configuration scope store:
+ // convenience to allow users with different workspaces to reuse the settings
+ ScopedPreferenceStore configurationStore = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, Activator.BUNDLE_ID);
+ registerSettingsChangeListener(configurationStore, settings,
+ ImmutableSet.of(RedmineAERISettingsPackage.eINSTANCE.getRedmineAERISettings_ApiKey()));
+
+ // set this settings object in the root context. Effectively replaces this context function.
+ IEclipseContext systemContext = SystemControl.getSystemContext();
+ systemContext.set(contextKey, settings);
+ return settings;
+ }
+
+ private static void registerSettingsChangeListener(final ScopedPreferenceStore store, final RedmineAERISettings settings,
+ final Set<EAttribute> allowedKeys) {
+ settings.eAdapters().add(new AdapterImpl() {
+ @Override
+ public void notifyChanged(Notification msg) {
+ Object feature = msg.getFeature();
+ if (!(feature instanceof EAttribute)) {
+ return;
+ }
+ EAttribute attr = (EAttribute) feature;
+ EDataType type = attr.getEAttributeType();
+ Object value = msg.getNewValue();
+
+ // @Nullable
+ String data = EcoreUtil.convertToString(type, value);
+
+ // if empty all keys are allowed:
+ if (allowedKeys.isEmpty() || allowedKeys.contains(attr)) {
+ try {
+ // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=494098,
+ // data may be null and null is not valid as value
+ if (data == null) {
+ store.setToDefault(attr.getName());
+ } else {
+ store.putValue(attr.getName(), data);
+ }
+ store.save();
+ } catch (Exception e) {
+ e.printStackTrace();
+// log(ERROR_SAVE_PREFERENCES_FAILED, e, attr.getName(), data);
+ }
+ }
+ }
+ });
+ }
+
+ private static void registerPreferenceStoreChangeListener(final ScopedPreferenceStore store, final RedmineAERISettings settings,
+ final EClass eClass) {
+ store.addPropertyChangeListener(new IPropertyChangeListener() {
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ String property = event.getProperty();
+ EStructuralFeature feature = eClass.getEStructuralFeature(property);
+ if (feature != null && feature instanceof EAttribute) {
+ EAttribute attr = (EAttribute) feature;
+ EDataType type = attr.getEAttributeType();
+ String string = EcoreUtil.convertToString(type, event.getNewValue());
+ Object value = EcoreUtil.createFromString(type, string);
+ settings.eSet(feature, value);
+ }
+ }
+ });
+ }
+
+ private static void loadFromPreferences(final ScopedPreferenceStore store, final RedmineAERISettings settings, final EClass eClass) {
+ for (EAttribute attr : eClass.getEAllAttributes()) {
+ EDataType type = attr.getEAttributeType();
+ String key = attr.getName();
+ if (!store.contains(key)) {
+ continue;
+ }
+ String value = store.getString(key);
+ try {
+ if (attr.isMany()) {
+ for (String s : convert(value)) {
+ Object data = EcoreUtil.createFromString(type, s);
+ ((List<Object>) settings.eGet(attr)).add(data);
+ }
+ continue;
+ }
+ Object data = EcoreUtil.createFromString(type, value);
+ settings.eSet(attr, data);
+ } catch (Exception e) {
+ e.printStackTrace();
+// log(ERROR_FAILED_TO_PARSE_PREFERENCE_VALUE, attr, value);
+ }
+ }
+ }
+
+ static List<String> convert(String string) {
+ return Splitter.on(';').omitEmptyStrings().trimResults().splitToList(string);
+
+ }
+
+ static String convert(List<String> strings) {
+ return Joiner.on(';').skipNulls().join(strings);
+ }
+}