blob: 46788a28bb0cca59b4d229f84639adb2f8663318 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.xml.ui.internal.contentoutline;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.xml.ui.internal.XMLUIMessages;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* This job holds a queue of updates (affected nodes) for multiple structured
* viewers. When a new request comes in, the current run is cancelled, the new
* request is added to the queue, then the job is re-scheduled. Support for
* multiple structured viewers is required because refresh updates are usually
* triggered by model changes, and the model may be visible in more than one
* viewer.
*
* @author pavery
*/
class RefreshStructureJob extends Job {
/** debug flag */
static final boolean DEBUG;
private static final long UPDATE_DELAY = 200;
static {
String value = Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/outline"); //$NON-NLS-1$
DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
}
/** List of refresh requests (Nodes) */
private final List fRequests;
/** the structured viewers */
List fViewers = new ArrayList(3);
public RefreshStructureJob() {
super(XMLUIMessages.refreshoutline_0); //$NON-NLS-1$
setPriority(Job.LONG);
setSystem(true);
fRequests = new ArrayList(1);
}
private synchronized void addRequest(Node node) {
// if we already have a request which contains the new request,
// discare the new request
int size = fRequests.size();
for (int i = 0; i < size; i++) {
if (contains((Node) fRequests.get(i), node))
return;
}
// if new request is contains any existing requests,
// remove those
for (Iterator it = fRequests.iterator(); it.hasNext();) {
if (contains(node, (Node) it.next()))
it.remove();
}
fRequests.add(node);
}
private synchronized void addViewer(StructuredViewer viewer) {
if (!fViewers.contains(viewer)) {
fViewers.add(viewer);
}
}
/**
* @return if the root is parent of possible, return true, otherwise
* return false
*/
private boolean contains(Node root, Node possible) {
if (DEBUG) {
System.out.println("=============================================================================================================="); //$NON-NLS-1$
System.out.println("recursive call w/ root: " + root.getNodeName() + " and possible: " + possible); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println("--------------------------------------------------------------------------------------------------------------"); //$NON-NLS-1$
}
// the following checks are important
// #document node will break the algorithm otherwise
// can't contain the parent if it's null
if (root == null) {
if (DEBUG)
System.out.println("returning false: root is null"); //$NON-NLS-1$
return false;
}
// nothing can be parent of Document node
if (possible instanceof Document) {
if (DEBUG)
System.out.println("returning false: possible is Document node"); //$NON-NLS-1$
return false;
}
// document contains everything
if (root instanceof Document) {
if (DEBUG)
System.out.println("returning true: root is Document node"); //$NON-NLS-1$
return true;
}
// depth first
Node current = root;
// loop siblings
while (current != null) {
if (DEBUG)
System.out.println(" -> iterating sibling (" + current.getNodeName() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
// found it
if (possible.equals(current)) {
if (DEBUG)
System.out.println(" !!! found: " + possible.getNodeName() + " in subtree for: " + root.getNodeName()); //$NON-NLS-1$ //$NON-NLS-2$
return true;
}
// drop one level deeper if necessary
if (current.getFirstChild() != null) {
return contains(current.getFirstChild(), possible);
}
current = current.getNextSibling();
}
// never found it
return false;
}
/**
* Refresh must be on UI thread because it's on a SWT widget.
*
* @param node
*/
private void doRefresh(final Node node) {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.asyncExec(new Runnable() {
public void run() {
if (DEBUG)
System.out.println("refresh on: [" + node.getNodeName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
StructuredViewer[] viewers = (StructuredViewer[]) fViewers.toArray(new StructuredViewer[0]);
fViewers.clear();
for (int i = 0; i < viewers.length; i++) {
if (!viewers[i].getControl().isDisposed()) {
if (node instanceof Document) {
viewers[i].refresh();
}
else {
viewers[i].refresh(node, true);
}
}
else {
if (DEBUG)
System.out.println(" !!! skipped refreshing disposed viewer: " + viewers[i]); //$NON-NLS-1$
}
}
}
});
}
/**
* This method also synchronized because it accesses the fRequests queue
*
* @return an array of the currently requested Nodes to refresh
*/
private synchronized Node[] getRequests() {
Node[] toRefresh = (Node[]) fRequests.toArray(new Node[fRequests.size()]);
fRequests.clear();
return toRefresh;
}
/**
* Invoke a refresh on the viewer on the given node.
*
* @param node
*/
public void refresh(StructuredViewer viewer, Node node) {
if (node == null)
return;
cancel();
addRequest(node);
addViewer(viewer);
schedule(UPDATE_DELAY);
}
protected IStatus run(IProgressMonitor monitor) {
IStatus status = Status.OK_STATUS;
try {
Node[] toRefresh = getRequests();
for (int i = 0; i < toRefresh.length; i++) {
if (monitor.isCanceled())
throw new OperationCanceledException();
doRefresh(toRefresh[i]);
}
}
finally {
monitor.done();
}
return status;
}
}