| /******************************************************************************* |
| * Copyright (c) 2007, 2008 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0, which accompanies this distribution |
| * and is available at http://www.eclipse.org/legal/epl-v10.html. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.utility.internal.model.value.prefs; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.prefs.BackingStoreException; |
| import java.util.prefs.PreferenceChangeEvent; |
| import java.util.prefs.PreferenceChangeListener; |
| import java.util.prefs.Preferences; |
| import org.eclipse.jpt.utility.internal.iterators.ArrayIterator; |
| import org.eclipse.jpt.utility.internal.iterators.TransformationIterator; |
| import org.eclipse.jpt.utility.internal.model.value.AspectAdapter; |
| import org.eclipse.jpt.utility.internal.model.value.StaticPropertyValueModel; |
| import org.eclipse.jpt.utility.model.listener.ChangeListener; |
| import org.eclipse.jpt.utility.model.listener.CollectionChangeListener; |
| import org.eclipse.jpt.utility.model.value.CollectionValueModel; |
| import org.eclipse.jpt.utility.model.value.PropertyValueModel; |
| |
| /** |
| * This adapter wraps a Preferences node and converts its preferences into a |
| * CollectionValueModel of PreferencePropertyValueModels. It listens for |
| * "preference" changes and converts them into VALUE collection changes. |
| */ |
| public class PreferencesCollectionValueModel<P> |
| extends AspectAdapter<Preferences> |
| implements CollectionValueModel<PreferencePropertyValueModel<P>> |
| { |
| |
| /** Cache the current preferences, stored in models and keyed by name. */ |
| protected final HashMap<String, PreferencePropertyValueModel<P>> preferences; |
| |
| /** A listener that listens to the preferences node for added or removed preferences. */ |
| protected final PreferenceChangeListener preferenceChangeListener; |
| |
| |
| // ********** constructors ********** |
| |
| /** |
| * Construct an adapter for the specified preferences node. |
| */ |
| public PreferencesCollectionValueModel(Preferences preferences) { |
| this(new StaticPropertyValueModel<Preferences>(preferences)); |
| } |
| |
| /** |
| * Construct an adapter for the specified preferences node. |
| */ |
| public PreferencesCollectionValueModel(PropertyValueModel<? extends Preferences> preferencesHolder) { |
| super(preferencesHolder); |
| this.preferences = new HashMap<String, PreferencePropertyValueModel<P>>(); |
| this.preferenceChangeListener = this.buildPreferenceChangeListener(); |
| } |
| |
| |
| // ********** initialization ********** |
| |
| /** |
| * A preferences have changed, notify the listeners. |
| */ |
| protected PreferenceChangeListener buildPreferenceChangeListener() { |
| // transform the preference change events into VALUE collection change events |
| return new PreferenceChangeListener() { |
| public void preferenceChange(PreferenceChangeEvent event) { |
| PreferencesCollectionValueModel.this.preferenceChanged(event.getKey(), event.getNewValue()); |
| } |
| @Override |
| public String toString() { |
| return "preference change listener"; |
| } |
| }; |
| } |
| |
| |
| // ********** CollectionValueModel implementation ********** |
| |
| /** |
| * Return an iterator on the preference models. |
| */ |
| public synchronized Iterator<PreferencePropertyValueModel<P>> iterator() { |
| return this.preferences.values().iterator(); |
| } |
| |
| public synchronized int size() { |
| return this.preferences.size(); |
| } |
| |
| |
| // ********** AspectAdapter implementation ********** |
| |
| @Override |
| protected Object getValue() { |
| return this.iterator(); |
| } |
| |
| @Override |
| protected Class<? extends ChangeListener> getListenerClass() { |
| return CollectionChangeListener.class; |
| } |
| |
| @Override |
| protected String getListenerAspectName() { |
| return VALUES; |
| } |
| |
| @Override |
| protected boolean hasListeners() { |
| return this.hasAnyCollectionChangeListeners(VALUES); |
| } |
| |
| @Override |
| protected void fireAspectChange(Object oldValue, Object newValue) { |
| this.fireCollectionChanged(VALUES); |
| } |
| |
| @Override |
| protected void engageSubject_() { |
| this.subject.addPreferenceChangeListener(this.preferenceChangeListener); |
| for (Iterator<PreferencePropertyValueModel<P>> stream = this.preferenceModels(); stream.hasNext(); ) { |
| PreferencePropertyValueModel<P> preferenceModel = stream.next(); |
| this.preferences.put(preferenceModel.getKey(), preferenceModel); |
| } |
| } |
| |
| @Override |
| protected void disengageSubject_() { |
| try { |
| this.subject.removePreferenceChangeListener(this.preferenceChangeListener); |
| } catch (IllegalStateException ex) { |
| // for some odd reason, we are not allowed to remove a listener from a "dead" |
| // preferences node; so handle the exception that gets thrown here |
| if ( ! ex.getMessage().equals("Node has been removed.")) { |
| // if it is not the expected exception, re-throw it |
| throw ex; |
| } |
| } |
| this.preferences.clear(); |
| } |
| |
| |
| // ********** AbstractModel implementation ********** |
| |
| @Override |
| public void toString(StringBuilder sb) { |
| sb.append(this.subject); |
| } |
| |
| |
| // ********** internal methods ********** |
| |
| /** |
| * Return an iterator on the preference models. |
| * At this point we can be sure that the subject is not null. |
| */ |
| protected Iterator<PreferencePropertyValueModel<P>> preferenceModels() { |
| String[] keys; |
| try { |
| keys = this.subject.keys(); |
| } catch (BackingStoreException ex) { |
| throw new RuntimeException(ex); |
| } |
| return new TransformationIterator<String, PreferencePropertyValueModel<P>>(new ArrayIterator<String>(keys)) { |
| @Override |
| protected PreferencePropertyValueModel<P> transform(String key) { |
| return PreferencesCollectionValueModel.this.buildPreferenceModel(key); |
| } |
| }; |
| } |
| |
| /** |
| * Override this method to tweak the model used to wrap the |
| * specified preference (e.g. to customize the model's converter). |
| */ |
| protected PreferencePropertyValueModel<P> buildPreferenceModel(String key) { |
| return new PreferencePropertyValueModel<P>(this.subjectHolder, key); |
| } |
| |
| protected synchronized void preferenceChanged(String key, String newValue) { |
| if (newValue == null) { |
| // a preference was removed |
| PreferencePropertyValueModel<P> preferenceModel = this.preferences.remove(key); |
| this.fireItemRemoved(VALUES, preferenceModel); |
| } else if ( ! this.preferences.containsKey(key)) { |
| // a preference was added |
| PreferencePropertyValueModel<P> preferenceModel = this.buildPreferenceModel(key); |
| this.preferences.put(key, preferenceModel); |
| this.fireItemAdded(VALUES, preferenceModel); |
| } else { |
| // a preference's value changed - do nothing |
| } |
| } |
| |
| } |