/*=============================================================================#
# Copyright (c) 2008-2016 Stephan Wahlbrink (WalWare.de) 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
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.ecommons.ltk.core.impl;
import java.util.HashMap;
import java.util.LinkedList;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import de.walware.ecommons.ltk.ElementChangedEvent;
import de.walware.ecommons.ltk.IElementChangedListener;
import de.walware.ecommons.ltk.IModelElementDelta;
import de.walware.ecommons.ltk.LTK;
import de.walware.ecommons.ltk.LTKUtil;
import de.walware.ecommons.ltk.WorkingContext;
import de.walware.ecommons.ltk.core.model.IModelElement;
import de.walware.ecommons.ltk.core.model.ISourceUnit;
import de.walware.ecommons.ltk.core.model.ISourceUnitModelInfo;
import de.walware.ecommons.ltk.internal.core.LTKCorePlugin;
/**
* Abstract model update event job
*/
public abstract class AbstractModelEventJob<ElementType extends IModelElement, InfoType extends ISourceUnitModelInfo> extends Job {
private static class SafeRunnable implements ISafeRunnable {
final ElementChangedEvent fEvent;
IElementChangedListener fListener;
public SafeRunnable(final ElementChangedEvent event) {
fEvent = event;
}
@Override
public void run() {
fListener.elementChanged(fEvent);
}
@Override
public void handleException(final Throwable e) {
LTKCorePlugin.log(new Status(IStatus.ERROR, LTK.PLUGIN_ID, -1,
"An error occured while notifying an ElementChangedListener.", e )); //$NON-NLS-1$
}
}
protected class Task {
private final ElementType fElement;
private InfoType fOldInfo;
private InfoType fNewInfo;
public Task(final ElementType element) {
fElement = element;
}
public ElementType getElement() {
return fElement;
}
public InfoType getOldInfo() {
return fOldInfo;
}
public InfoType getNewInfo() {
return fNewInfo;
}
void run() {
final IModelElementDelta delta = createDelta(this);
fireDelta(delta);
}
}
private final AbstractModelManager fModelManager;
private final Object fTasksLock = new Object();
private final LinkedList<IModelElement> fTaskQueue = new LinkedList<>();
private final HashMap<IModelElement, Task> fTaskDetail = new HashMap<>();
private boolean fWorking = false;
private boolean fStop = false;
public AbstractModelEventJob(final AbstractModelManager manager) {
super("Model Events for " + manager.getModelTypeId()); //$NON-NLS-1$
setPriority(BUILD);
setSystem(true);
setUser(false);
fModelManager = manager;
}
public void addUpdate(final ElementType element,
final InfoType oldModel, final InfoType newModel) {
synchronized (fTasksLock) {
Task task = fTaskDetail.get(element);
if (task == null) {
task = new Task(element);
task.fOldInfo = oldModel;
fTaskDetail.put(element, task);
}
else {
fTaskQueue.remove(element);
}
task.fNewInfo = newModel;
fTaskQueue.add(element);
if (!fWorking) {
schedule();
}
}
}
protected abstract IModelElementDelta createDelta(Task task);
@Override
protected IStatus run(final IProgressMonitor monitor) {
while (true) {
Task task;
synchronized (fTasksLock) {
final IModelElement element = (!fTaskQueue.isEmpty()) ? fTaskQueue.removeFirst() : null;
if (element == null || fStop) {
fWorking = false;
return Status.OK_STATUS;
}
fWorking = true;
task = fTaskDetail.remove(element);
}
try {
task.run();
}
catch (final Throwable e) {
LTKCorePlugin.log(new Status(IStatus.ERROR, LTK.PLUGIN_ID, -1,
"An error occurred when firing model event for " + fModelManager.getModelTypeId() + ".", //$NON-NLS-1$
e ));
}
}
}
protected void dispose() {
synchronized (fTasksLock) {
fStop = true;
fTaskQueue.clear();
fTaskDetail.clear();
}
}
private void fireDelta(final IModelElementDelta delta) {
final ISourceUnit su = LTKUtil.getSourceUnit(delta.getModelElement());
if (su == null) {
return;
}
final WorkingContext context = su.getWorkingContext();
final ElementChangedEvent event = new ElementChangedEvent(delta, context);
final SafeRunnable runnable = new SafeRunnable(event);
final IElementChangedListener[] listeners = fModelManager.getElementChangedListeners(context);
for (int i = 0; i < listeners.length; i++) {
runnable.fListener = listeners[i];
SafeRunner.run(runnable);
}
}
}