/*
* Copyright 2013 Serdar.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.fub.utilsmodule.synchronizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.util.ChangeSupport;
import org.openide.util.WeakSet;
/**
* Model Synchronizer to synchonizer the visual representation with the
* underlying fileObject.
*
* @author Serdar
*/
public abstract class ModelSynchronizer {
// set for weak references
private final WeakSet<ModelSynchronizerClient> listenerSet = new WeakSet<ModelSynchronizerClient>();
// map to hold order information in which the mscs are created
private final SortedMap<Integer, ModelSynchronizerClient> listenerMap = new TreeMap<Integer, ModelSynchronizerClient>();
private final Object MUTEX = new Object();
private final HashMap<ChangeListener, ModelSynchronizerClient> listener = new HashMap<ChangeListener, ModelSynchronizerClient>();
/**
* Notifies all clients, except the one which caused the notification.
*
* @param client
*/
public final void modelChanged(ModelSynchronizerClient client) {
final List<Entry<Integer, ModelSynchronizerClient>> clients = new ArrayList<Entry<Integer, ModelSynchronizerClient>>();
synchronized (MUTEX) {
// get current msc from the map
HashSet<ModelSynchronizerClient> hashSet = new HashSet<ModelSynchronizerClient>(listenerMap.values());
// filter all mscs that are already GC
for (ModelSynchronizerClient modelSynchronizerClient : hashSet) {
if (!listenerSet.contains(modelSynchronizerClient)) {
listenerMap.values().remove(modelSynchronizerClient);
}
}
clients.addAll(listenerMap.entrySet());
}
// iterate in order through all mscs
for (Entry<Integer, ModelSynchronizerClient> entry : clients) {
if (!entry.getValue().equals(client)) {
entry.getValue().updateModel();
}
}
}
/**
* Creates a ModelSynchonizerClient for the specified ChangeListener.
*
* @param changeListener
* @return ModelSynchronizerClient instance.
*/
public final ModelSynchronizerClient create(ChangeListener changeListener) {
if (!listener.containsKey(changeListener)) {
synchronized (MUTEX) {
ModelSynchronizerClient modelSynchronizerClient = new ModelSynchronizerClient(changeListener, ModelSynchronizer.this);
listenerSet.add(modelSynchronizerClient);
listenerMap.put(listenerSet.size() - 1, modelSynchronizerClient);
listener.put(changeListener, modelSynchronizerClient);
}
}
return listener.get(changeListener);
}
protected void synchronize() {
updateSource();
}
/**
* Updated the underlying object, i.e. a fileObject.
*/
public abstract void updateSource();
public static class ModelSynchronizerClient {
private final ChangeSupport changeSupport = new ChangeSupport(this);
private final ChangeListener listener;
private final ModelSynchronizer synchornizer;
private ModelSynchronizerClient(ChangeListener listener, ModelSynchronizer synchronizer) {
assert listener != null;
assert synchronizer != null;
this.listener = listener;
this.synchornizer = synchronizer;
changeSupport.addChangeListener(listener);
}
public void modelChangedFromGui() {
synchornizer.updateSource();
}
public void modelChangedFromSource() {
synchornizer.modelChanged(ModelSynchronizerClient.this);
}
protected void updateModel() {
listener.stateChanged(new ChangeEvent(synchornizer));
}
}
}