package org.erlide.engine.internal.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IResourceDelta;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.internal.model.root.ErlElementDelta;
import org.erlide.engine.model.IErlElement;
import org.erlide.engine.model.root.ElementChangedEvent;
import org.erlide.engine.model.root.IElementChangedListener;
import org.erlide.engine.model.root.IErlElementDelta;
import org.erlide.engine.model.root.IErlModule;
public class ErlModelDeltaManager {
public static final int DEFAULT_CHANGE_EVENT = 0;
private static final boolean verbose = false;
/**
* Turns delta firing on/off. By default it is on.
*/
public boolean fFire;
/**
* Queue of reconcile deltas on working copies that have yet to be fired.
* This is a table form IWorkingCopy to IErlElementDelta
*/
public Map<IErlModule, IErlElementDelta> reconcileDeltas;
/**
* Queue of deltas created explicitly by the model that have yet to be
* fired.
*/
public List<IErlElementDelta> erlModelDeltas;
private final ErlModel model;
public ErlModelDeltaManager(final ErlModel model) {
this.model = model;
fFire = true;
reconcileDeltas = new HashMap<>();
erlModelDeltas = Collections.synchronizedList(new ArrayList<IErlElementDelta>());
}
/**
* Fire Model deltas, flushing them after the fact. If the firing mode has
* been turned off, this has no effect.
*/
protected void fire(final IErlElementDelta customDeltas, final int eventType) {
if (fFire) {
IErlElementDelta deltaToNotify;
if (customDeltas == null) {
deltaToNotify = mergeDeltas(erlModelDeltas);
} else {
deltaToNotify = customDeltas;
}
final IElementChangedListener[] listeners;
final int listenerCount;
final int[] listenerMask;
// Notification
synchronized (model.elementChangedListeners) {
listeners = new IElementChangedListener[model.elementChangedListeners
.size()];
model.elementChangedListeners.toArray(listeners);
listenerCount = listeners.length;
listenerMask = null;
}
switch (eventType) {
case DEFAULT_CHANGE_EVENT:
// firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask,
// listenerCount);
firePostChangeDelta(deltaToNotify, listeners, listenerMask,
listenerCount);
fireReconcileDelta(listeners, listenerMask, listenerCount);
break;
// case ElementChangedEvent.PRE_AUTO_BUILD :
// firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask,
// listenerCount);
// break;
case ElementChangedEvent.POST_CHANGE:
firePostChangeDelta(deltaToNotify, listeners, listenerMask,
listenerCount);
fireReconcileDelta(listeners, listenerMask, listenerCount);
break;
case ElementChangedEvent.POST_RECONCILE:
fireReconcileDelta(listeners, listenerMask, listenerCount);
break;
case ElementChangedEvent.POST_SHIFT:
fireShiftEvent(deltaToNotify, listeners, listenerMask, listenerCount);
return;
}
}
}
private void firePostChangeDelta(final IErlElementDelta deltaToNotify,
final IElementChangedListener[] listeners, final int[] listenerMask,
final int listenerCount) {
// post change deltas
if (verbose) {
System.out.println(
"FIRING POST_CHANGE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
System.out
.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
}
if (deltaToNotify != null) {
// flush now so as to keep listener reactions to post their own
// deltas for
// subsequent iteration
flushDeltas();
model.notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE,
listeners, listenerMask, listenerCount);
}
}
private void fireReconcileDelta(final IElementChangedListener[] listeners,
final int[] listenerMask, final int listenerCount) {
final IErlElementDelta deltaToNotify = mergeDeltas(reconcileDeltas.values());
if (verbose) {
System.out.println(
"FIRING POST_RECONCILE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
System.out
.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
}
if (deltaToNotify != null) {
// flush now so as to keep listener reactions to post their own
// deltas for
// subsequent iteration
reconcileDeltas.clear();
model.notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE,
listeners, listenerMask, listenerCount);
}
}
private void fireShiftEvent(final IErlElementDelta deltaToNotify,
final IElementChangedListener[] listeners, final int[] listenerMask,
final int listenerCount) {
// post change deltas
if (verbose) {
System.out
.println("FIRING POST_SHIFT event [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
System.out
.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
}
if (deltaToNotify != null) {
flushDeltas();
model.notifyListeners(deltaToNotify, ElementChangedEvent.POST_SHIFT,
listeners, listenerMask, listenerCount);
}
}
/**
* Flushes all deltas without firing them.
*/
protected void flushDeltas() {
erlModelDeltas.clear();
}
IErlElementDelta mergeDeltas(final Collection<IErlElementDelta> deltas) {
synchronized (deltas) {
if (deltas.isEmpty()) {
return null;
}
if (deltas.size() == 1) {
return deltas.iterator().next();
}
final Iterator<IErlElementDelta> iterator = deltas.iterator();
final IErlElement cRoot = ErlangEngine.getInstance().getModel();
final ErlElementDelta rootDelta = new ErlElementDelta(0, 0, cRoot);
boolean insertedTree = false;
while (iterator.hasNext()) {
final ErlElementDelta delta = (ErlElementDelta) iterator.next();
final IErlElement element = delta.getElement();
if (cRoot.equals(element)) {
final IErlElementDelta[] children = delta
.getChildren(IErlElementDelta.ALL);
for (final IErlElementDelta element0 : children) {
final ErlElementDelta projectDelta = (ErlElementDelta) element0;
rootDelta.insertDeltaTree(projectDelta.getElement(),
projectDelta);
insertedTree = true;
}
final IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
if (resourceDeltas != null) {
for (final IResourceDelta element0 : resourceDeltas) {
rootDelta.addResourceDelta(element0);
insertedTree = true;
}
}
} else {
rootDelta.insertDeltaTree(element, delta);
insertedTree = true;
}
}
if (insertedTree) {
return rootDelta;
}
return null;
}
}
}