/*******************************************************************************
* Copyright (c) 2011, 2014 Wind River Systems, 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
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.ui.views.events;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.tcf.te.runtime.model.interfaces.IModelNode;
import org.eclipse.tcf.te.runtime.model.interfaces.IModelNodeProvider;
import org.eclipse.tcf.te.ui.swt.DisplayUtil;
import org.eclipse.tcf.te.ui.views.interfaces.IUIConstants;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.navigator.CommonViewer;
/**
* Abstract UI event listener updating the main view.
*/
public abstract class AbstractEventListener extends org.eclipse.tcf.te.ui.events.AbstractEventListener {
// Reference to the viewer instance
private CommonViewer viewer = null;
// Reference to the refresh job
private RefreshJob refreshJob = null;
// Reference to the update job
private UpdateJob updateJob = null;
/**
* Returns the main view.
*
* @return The main view or <code>null</code>.
*/
protected CommonViewer getViewer() {
if (viewer == null && PlatformUI.isWorkbenchRunning()) {
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench != null ? workbench.getActiveWorkbenchWindow() : null;
IWorkbenchPage page = window != null ? window.getActivePage() : null;
if (page != null) {
IViewPart part = page.findView(getCommonNavigatorPartId());
if (part instanceof CommonNavigator) {
viewer = ((CommonNavigator)part).getCommonViewer();
}
}
}
return viewer;
}
/**
* Returns the part id of the common navigator view to refresh.
*
* @return The part id of the common navigator view to refresh-
*/
protected String getCommonNavigatorPartId() {
return IUIConstants.ID_EXPLORER;
}
/**
* Checks the viewers selection if it needs to get re-applied in order to
* trigger a selection changed event.
*
* @param viewer The common viewer. Must not be <code>null</code>.
* @param node The refreshed node or <code>null</code> if the whole tree is refreshed.
*/
protected void triggerSelectionChanged(CommonViewer viewer, Object node) {
Assert.isNotNull(viewer);
// If the whole tree is refreshed, it is simple
if (node == null) {
ISelection selection = viewer.getSelection();
if (selection != null && !selection.isEmpty()) {
viewer.setSelection(selection, true);
}
} else {
// Analyze the selection if the node refresh is part of it
ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
Iterator<?> iterator = ((IStructuredSelection)selection).iterator();
while (iterator.hasNext()) {
Object selected = iterator.next();
boolean apply = false;
if (node.equals(selected)) apply = true;
if (!apply) {
// Check if we can adapt to the class type of the selected node
Object adapted = selected instanceof IAdaptable ? ((IAdaptable)selected).getAdapter(node.getClass()) : null;
if (adapted == null) adapted = Platform.getAdapterManager().getAdapter(selected, node.getClass());
if (adapted != null && adapted.equals(node)) apply = true;
}
// Apply the selection
if (apply) {
viewer.setSelection(selection, true);
break;
}
}
}
}
}
/**
* Trigger a refresh of the given node. If the node
* is <code>null</code>, everything will be refreshed.
*
* @param node The node or <code>null</code>.
* @param scheduled <code>True</code> to schedule the refresh for asynchronous execution, <code>false</code> to
* execute the refresh synchronously.
*
* @see CommonViewer#refresh()
* @see CommonViewer#refresh(Object)
*/
protected void refresh(Object node, boolean scheduled) {
CommonViewer viewer = getViewer();
if (viewer == null || (viewer.getControl() != null && viewer.getControl().isDisposed())) return;
if (scheduled) {
scheduleRefreshJob(node != null ? node : viewer, viewer);
} else {
refresh(viewer, node);
}
}
/**
* Check for the viewer busy action and fire the refresh asynchronously
* if needed.
*
* @param viewer The viewer. Must not be <code>null</code>.
* @param node The node to refresh or <code>null</code>.
*/
private void refresh(final CommonViewer viewer, final Object node) {
Assert.isNotNull(viewer);
Runnable runnable = new Runnable() {
@Override
public void run() {
if (node != null) {
viewer.refresh(node);
} else {
viewer.refresh();
}
// Trigger a selection changed event if needed
triggerSelectionChanged(viewer, node);
}
};
if (viewer.isBusy()) {
DisplayUtil.safeAsyncExec(runnable);
} else {
runnable.run();
}
}
/**
* Trigger a update of the given node.
*
* @param node The node. Must not be <code>null</code>.
* @param scheduled <code>True</code> to schedule the update for asynchronous execution, <code>false</code> to
* execute the update synchronously.
*
* @see CommonViewer#update(Object, String[])
*/
protected void update(Object node, boolean scheduled) {
Assert.isNotNull(node);
CommonViewer viewer = getViewer();
if (viewer == null || (viewer.getControl() != null && viewer.getControl().isDisposed())) return;
if (scheduled) {
scheduleUpdateJob(node, viewer);
} else {
viewer.update(node, null);
// Trigger a selection changed event if needed
triggerSelectionChanged(viewer, node);
}
}
private static final int SCHEDULE_TIME = 1000;
/**
* Abstract refresh or update job implementation
*/
private abstract class AbstractJob extends Job {
/* default */ final CommonViewer parentViewer;
private final Queue<Object> nodes = new ConcurrentLinkedQueue<Object>();
private boolean done = true;
/**
* Constructor.
*
* @param name The job name.
* @param viewer The viewer instance. Must not be <code>null</code>.
*/
protected AbstractJob(String name, CommonViewer viewer) {
super(name);
Assert.isNotNull(viewer);
this.parentViewer = viewer;
setPriority(Job.SHORT);
setSystem(true);
}
/**
* Adds the given node to the job.
*
* @param element The element to add or <code>null</code> to refresh everything.
*/
protected void addNode(Object element) {
// if whole tree should be refreshed, clear the queue
if (element instanceof CommonViewer) {
nodes.clear();
}
// if the element to refresh is not in list
else if (!nodes.contains(element)) {
// if model node look at parent/child relationship
if (element instanceof IModelNodeProvider) {
IModelNode node = ((IModelNodeProvider)element).getModelNode();
Iterator<Object> it = nodes.iterator();
while (it.hasNext() && element != null && node != null) {
Object obj = it.next();
if (obj instanceof IModelNodeProvider) {
IModelNode rNode = ((IModelNodeProvider)obj).getModelNode();
if (rNode != null) {
// if parent already in list -> skip
if (rNode.equals(node.getParent())) {
element = null;
}
// if child in list -> remove child
else if (node.equals(rNode.getParent())) {
it.remove();
}
}
}
}
}
}
// skip element if already in list
else {
element = null;
}
// add to list if not skipped
if (element != null) nodes.add(element);
// if job is not scheduled, reschedule it
if (done) {
done = false;
this.schedule(SCHEDULE_TIME);
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
Object node = nodes.poll();
while (node != null) {
Runnable runnable = newRunnable(node);
if (runnable != null) {
try {
PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
}
catch (Exception e) {
// if display is disposed, silently ignore.
}
}
// get the next element to refresh
node = nodes.poll();
}
// set job to done so the next add would reschedule it
done = true;
return Status.OK_STATUS;
}
/**
* Creates the runnable.
*
* @param node The node. Must not be <code>null</code>.
* @return The runnable or <code>null</code>
*/
protected Runnable newRunnable(final Object node) {
Assert.isNotNull(node);
return null;
}
}
/**
* Refresh Job implementation.
*/
private class RefreshJob extends AbstractJob {
/**
* Constructor.
*
* @param viewer The viewer instance. Must not be <code>null</code>.
*/
public RefreshJob(CommonViewer viewer) {
super(RefreshJob.class.getSimpleName(), viewer);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.views.events.AbstractEventListener.AbstractJob#newRunnable(java.lang.Object)
*/
@Override
protected Runnable newRunnable(final Object node) {
return new Runnable() {
@Override
public void run() {
if (parentViewer != null && parentViewer.getControl() != null && !parentViewer.getControl().isDisposed()) {
if (node instanceof CommonViewer) {
parentViewer.refresh();
} else {
parentViewer.refresh(node);
}
// Trigger a selection changed event if needed
triggerSelectionChanged(parentViewer, node);
}
}
};
}
}
/**
* Update Job implementation.
*/
private class UpdateJob extends AbstractJob {
/**
* Constructor.
*
* @param viewer The viewer instance. Must not be <code>null</code>.
*/
public UpdateJob(CommonViewer viewer) {
super(UpdateJob.class.getSimpleName(), viewer);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.views.events.AbstractEventListener.AbstractJob#newRunnable(java.lang.Object)
*/
@Override
protected Runnable newRunnable(final Object node) {
return new Runnable() {
@Override
public void run() {
if (parentViewer != null && parentViewer.getControl() != null && !parentViewer.getControl().isDisposed()) {
if (node instanceof CommonViewer) {
parentViewer.refresh();
} else {
parentViewer.update(node, null);
}
// Trigger a selection changed event if needed
triggerSelectionChanged(parentViewer, node);
}
}
};
}
}
/**
* Schedule the asynchronous refresh job.
*
* @param node The node. Must not be <code>null</code>.
* @param viewer The viewer instance. Must not be <code>null</code>.
*/
private void scheduleRefreshJob(Object node, CommonViewer viewer) {
Assert.isNotNull(node);
Assert.isNotNull(viewer);
if (refreshJob == null) {
refreshJob = new RefreshJob(viewer);
}
refreshJob.addNode(node);
}
/**
* Schedule the asynchronous update job.
*
* @param node The node. Must not be <code>null</code>.
* @param viewer The viewer instance. Must not be <code>null</code>.
*/
private void scheduleUpdateJob(Object node, CommonViewer viewer) {
Assert.isNotNull(node);
Assert.isNotNull(viewer);
if (updateJob == null) {
updateJob = new UpdateJob(viewer);
}
updateJob.addNode(node);
}
}