package uihelpers; import helpers.StatusObject.ChangeType; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.eclipse.jface.viewers.TreeViewer; public class DelayedTreeUpdater<V> { private final TreeViewer viewer; private final Object synchUpdate = new Object(); private Set<DTEntry<V>> add = new HashSet<DTEntry<V>>(); private Map<Object,Set<V>> addInverse = new HashMap<Object,Set<V>>(); private Set<DTEntry<V>> update = new HashSet<DTEntry<V>>(); private Set<DTEntry<V>> remove = new HashSet<DTEntry<V>>(); private List<DTEntry<V>> delayed = new ArrayList<DTEntry<V>>(); private final int delay; public DelayedTreeUpdater(TreeViewer viewer) { this(viewer,500); } public DelayedTreeUpdater(TreeViewer viewer,int millisecDelay) { this.viewer = viewer; this.delay = millisecDelay; } public void add(V v,Object parent) { DTEntry<V> dt = new DTEntry<V>(parent, v,ChangeType.ADDED); synchronized(synchUpdate) { if (remove.contains(dt) || update.contains(dt)) { delayed.add(dt); } else { add.add(dt); addToMapSet(addInverse, parent, v); } /* if (remove.remove(dt)) { update.add(dt); } else { add.add(dt); addToMapSet(addInverse, parent, v); } */ } ensureUpdate(); } public void change(V v,Object parent) { DTEntry<V> dt = new DTEntry<V>(parent, v,ChangeType.CHANGED); synchronized(synchUpdate) { if (add.contains(dt) || remove.contains(dt)) { delayed.add(dt); } else { update.add(dt); } /* if (!add.contains(dt)) { update.add(dt); } */ } ensureUpdate(); } public void remove(V v,Object parent) { DTEntry<V> dt = new DTEntry<V>(parent, v,ChangeType.REMOVED); synchronized(synchUpdate) { if (add.contains(dt) || update.contains(dt)) { delayed.add(dt); } else { // if (!add.remove(dt)) { removeMapSet(addInverse, parent, v); remove.add(dt); } } ensureUpdate(); } public void put(ChangeType ct,V value,Object parent) { switch(ct) { case ADDED: add(value, parent); break; case CHANGED: change(value, parent); break; case REMOVED: remove(value, parent); break; } } private void ensureUpdate() { new SUIJob(viewer.getControl()) { //"bulkupdate" public void run() { synchronized(synchUpdate) { boolean done = false; while (!done) { if (!add.isEmpty()) { for (Entry<Object,Set<V>> e:addInverse.entrySet()) { viewer.add(e.getKey(), e.getValue().toArray()); } add.clear(); addInverse.clear(); } if (!update.isEmpty()) { viewer.update(toArray(update), null); //.toArray() update.clear(); } if (!remove.isEmpty()) { viewer.remove(toArray(remove) ); //.toArray() remove.clear(); } done = delayed.isEmpty(); if (!done) { List<DTEntry<V>> delayCopy = delayed; delayed = new ArrayList<DTEntry<V>>(); for (DTEntry<V> dt:delayCopy) { put(dt.ct,dt.value,dt.parent); } } } } //viewer.refresh(); updateDone(); } }.scheduleIfNotRunning(delay,this); } /*private Map<Object,List<V>> inverseAdd() { Map<Object,List<V>> inverseMap = new HashMap<Object,List<V>>(); for (Entry<V,Object> e: add.entrySet()) { addToMapList(inverseMap, e.getValue(), e.getKey()); } return inverseMap; } */ private static <V> void addToMapSet(Map<Object,Set<V>> map,Object o, V v) { Set<V> list = map.get(o); if (list == null) { list = new HashSet<V>(); map.put(o, list); } list.add(v); } private static <V> void removeMapSet(Map<Object,Set<V>> map,Object o, V v) { Set<V> list = map.get(o); if (v != null && list != null) { list.remove(v); } } /** * discards all running add/update/delete ops */ public void clear() { synchronized(synchUpdate) { add.clear(); update.clear(); remove.clear(); delayed.clear(); } } /** * method for overwriting.. called by UI thread.. */ protected void updateDone() {} private static <V> Object[] toArray(Collection<DTEntry<V>> v) { Object[] o = new Object[v.size()]; Iterator<DTEntry<V>> it = v.iterator(); for (int i = 0; i < o.length && it.hasNext(); i++) { o[i] = it.next().value; } return o; } private static class DTEntry<V> { private final Object parent; private final V value; private final ChangeType ct; public DTEntry(Object parent, V value,ChangeType ct) { super(); this.parent = parent; this.value = value; this.ct = ct; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DTEntry<?> other = (DTEntry<?>) obj; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } } }