blob: da47a04fef33d0884a7265a1c90ce50cdd5a24d2 [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 the outline.
* When a new request comes in, the current run is canceled, the new
* request is added to the queue, then the job is re-scheduled.
*
* @author pavery
*/
public class RefreshOutlineJob extends Job {
private static final long UPDATE_DELAY = 200;
/** List of refresh requests (Nodes)*/
private final List fRequests;
/** the tree viewer */
private final StructuredViewer fViewer;
/** debug flag */
private static final boolean DEBUG;
static {
String value = Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/outline"); //$NON-NLS-1$
DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
}
public RefreshOutlineJob(StructuredViewer viewer) {
super(XMLUIMessages.refreshoutline_0); //$NON-NLS-1$
setPriority(Job.LONG);
setSystem(true);
fRequests = new ArrayList(1);
fViewer = viewer;
}
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);
}
/**
* 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(Node node) {
if (node == null)
return;
cancel();
addRequest(node);
schedule(UPDATE_DELAY);
}
/**
* @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;
}
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;
}
/**
* 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$
if(node instanceof Document)
fViewer.refresh();
else
fViewer.refresh(node);
}
});
}
}