Skip to content

Instantly share code, notes, and snippets.

@vladdu
Last active August 29, 2015 14:15
Show Gist options
  • Save vladdu/9834a2a0b04ef0fbeb55 to your computer and use it in GitHub Desktop.
Save vladdu/9834a2a0b04ef0fbeb55 to your computer and use it in GitHub Desktop.
import org.eclipse.core.runtime.Path;
public interface IPreferenceNotifier {
void changed(Path path, String key, String newValue);
void added(Path path, String key);
void removed(Path path, String key);
}
/*******************************************************************************
* Copyright (C) 2010, 2015, Google Inc. and others
*
* 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
*******************************************************************************/
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.osgi.service.prefs.BackingStoreException;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
/**
* Watches changes to Eclipse's preference tree, and sends notifications for
* each change.
*
* @author [email protected] (Brian Chin)
* @author [email protected] (Vlad Dumitrescu)
*/
public class PreferenceWatcher {
private class WatcherListener implements INodeChangeListener,
IPreferenceChangeListener {
@Override
public void added(final NodeChangeEvent event) {
synchronized (lock) {
// It's possible that we've stopped watching, but the listeners
// have not yet been removed.
// Ensure that we're still watching before writing new data
if (currState != State.WATCHING) {
return;
}
final IEclipsePreferences childPrefs = (IEclipsePreferences) event
.getChild();
addListener(childPrefs);
notifier.added(new Path(event.getParent().absolutePath()), event
.getChild().name());
}
}
@Override
public void removed(final NodeChangeEvent event) {
synchronized (lock) {
if (currState != State.WATCHING) {
return;
}
recordedNodes.remove(event.getChild());
notifier.removed(new Path(event.getParent().absolutePath()), event
.getChild().name());
}
}
@Override
public void preferenceChange(final PreferenceChangeEvent event) {
synchronized (lock) {
if (currState != State.WATCHING) {
return;
}
notifier.changed(new Path(event.getNode().absolutePath()),
event.getKey(), (String) event.getNewValue());
}
}
}
private enum State {
IDLE, WATCHING, COMPLETE
}
private final Object lock = new Object();
private volatile State currState = State.IDLE;
private final WatcherListener listener = new WatcherListener();
private final Set<IEclipsePreferences> recordedNodes = Sets.newHashSet();
IPreferenceNotifier notifier;
/**
* Starts the process of watching changes to the eclipse preference tree.
* Watching may only occur once per recorder.
*
* @throws CoreException
* if there is a problem with the preference tree itself.
* @throws IllegalStateException
* if this object has already been used to record.
*/
public void startWatching(final IPreferenceNotifier aNotifier) throws CoreException {
synchronized (lock) {
Preconditions.checkState(currState == State.IDLE,
"Recorder object has already recorded preference changes.");
currState = State.WATCHING;
notifier = aNotifier;
this.addListeners(Platform.getPreferencesService().getRootNode());
}
}
private void addListeners(final IEclipsePreferences node) throws CoreException {
addListener(node);
try {
for (final String childName : node.childrenNames()) {
final IEclipsePreferences child = (IEclipsePreferences) node
.node(childName);
this.addListeners(child);
}
} catch (final BackingStoreException e) {
throw new CoreException(new Status(IStatus.ERROR, ModelPlugin.PLUGIN_ID,
"Could not read children for preference node", e));
}
}
/**
* Ends the watching process. No other changes to the preference tree are
* reported after this method is called.
*
* @throws IllegalStateException
* if {@code startRecording()} had not been previously called,
* or if watching has already completed on this object.
*/
public void endWatching() {
synchronized (lock) {
Preconditions.checkState(currState == State.WATCHING,
"Recorder object is not currently watching");
currState = State.COMPLETE;
notifier = null;
for (final IEclipsePreferences node : recordedNodes) {
node.removeNodeChangeListener(listener);
node.removePreferenceChangeListener(listener);
}
recordedNodes.clear();
}
}
/**
* Returns true if this object is currently watching.
*/
public boolean isWatching() {
return this.currState == State.WATCHING;
}
private void addListener(final IEclipsePreferences node) {
node.addNodeChangeListener(listener);
node.addPreferenceChangeListener(listener);
recordedNodes.add(node);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment