blob: 121e1f5109abee0b6b6bfba85239db8b43eee1f7 [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.sse.ui.views.contentoutline;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
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.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.wst.sse.ui.EditorPlugin;
import org.eclipse.wst.sse.ui.nls.ResourceHandler;
import org.w3c.dom.Node;
public class RefreshOutlineJob extends Job {
/**
* Tree refresh must be done on UI Thread so UIJob is used
*/
public class RefreshJob extends UIJob {
StructuredViewer viewer = null;
public RefreshJob(StructuredViewer structuredViewer) {
// might need a different label, like "Refreshing Outline"
super(ResourceHandler.getString("JFaceNodeAdapter.0"));
this.viewer = structuredViewer;
}
public IStatus runInUIThread(IProgressMonitor monitor) {
IStatus status = Status.OK_STATUS;
//monitor.beginTask("", IProgressMonitor.UNKNOWN);
try {
Control control = this.viewer.getControl();
// we should have check before even scheduling this, but even
// if
// ok then, need to check again, right before executing.
if (control != null && !control.isDisposed()) {
Node smallest = getSmallestParent();
if (smallest == null || smallest.getParentNode() == null) {
// refresh whole document
this.viewer.refresh();
if (DEBUG)
System.out.println("refreshing viewer (entire tree)");
} else {
// refresh only the node that's changed
this.viewer.refresh(smallest);
if (DEBUG)
System.out.println("refreshing viewer on (" + smallest + ")");
}
}
} catch (Exception e) {
status = errorStatus(e);
} finally {
monitor.done();
}
return status;
}
}
// for debugging
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$
}
private Node fPossibleParent = null;
private Set fPruned = null;
private Node fSmallestParent = null;
// end RefreshJob class
private StructuredViewer fStructuredViewer;
/**
* name is the name of the Job (like which out line is being updated)
* possibleParent cannot be null.
*
* @param name
* @param structuredViewer
* @param currentParent
* @param possibleParent
*/
public RefreshOutlineJob(String name, StructuredViewer structuredViewer, Node currentParent, Node possibleParent) {
super(name);
setPriority(Job.SHORT);
setSystem(true);
fPruned = new HashSet();
setPossibleParent(possibleParent);
if (currentParent == null)
setSmallestParent(fPossibleParent);
setStructuredViewer(structuredViewer);
}
// each pruned node represents a subtree
private void addPruned(Node n) {
if (DEBUG)
System.out.println("(-) pruned node: " + n.getNodeName());
fPruned.add(n);
}
/**
* Calculates minimal common parent between the previous parent (passed in
* constructor) and the notifier (also passed in the constructor), and
* calls setSmallsetParent(...) with the result.
*/
private void calculateSmallestCommonParent() {
// quick checks
if (getPossibleParent() == null)
return;
// document root
if (getPossibleParent().getParentNode() == null) {
setSmallestParent(getPossibleParent());
if (DEBUG)
System.out.println("*** found smallest parent for refresh: " + getPossibleParent());
return;
}
// we already have document root
if (getSmallestParent() != null && getSmallestParent().getParentNode() == null) {
if (DEBUG)
System.out.println("*** already have smallest parent for refresh: " + getSmallestParent());
return;
}
// check one side
if (!contains(getPossibleParent(), getSmallestParent())) {
addPruned(getPossibleParent());
// check other side
if (!contains(getSmallestParent(), getPossibleParent())) {
addPruned(getSmallestParent());
// keep climbing up and searching now
Node parent = getSmallestParent();
while (parent != null) {
// check subtree
if (contains(parent, getPossibleParent())) {
// we found it, it's parent
setSmallestParent(parent);
if (DEBUG)
System.out.println("*** found smallest parent for refresh: " + parent);
break;
} else {
// not in this subtree
addPruned(parent);
}
parent = parent.getParentNode();
}
} else {
// its smaller parent (we already have it)
}
} else {
// it's the new possible one
if (DEBUG)
System.out.println("*** found smallest parent for refresh: " + getPossibleParent());
setSmallestParent(getPossibleParent());
}
}
/**
* @return if the root is parent of possible, return true, otherwise
* return false
*/
protected boolean contains(Node root, Node possible) {
if (DEBUG) {
System.out.println("==============================================================================================================");
System.out.println("recursive call w/ root: " + root.getNodeName() + " and possible " + possible);
System.out.println("--------------------------------------------------------------------------------------------------------------");
}
// can't contain the parent if it's null
if (root == null)
return false;
// it's the root node
if (root.getParentNode() == null)
return true;
// no parent set yet
if (getSmallestParent() == null)
return true;
// depth first
Node current = root;
// loop siblings
while (current != null) {
if (DEBUG)
System.out.println(" -> iterating sibling (" + current.getNodeName() + ")");
// found it
if (possible.equals(current)) {
if (DEBUG)
System.out.println(" !!! found: " + possible.getNodeName() + " in subtree for: " + root.getNodeName());
return true;
}
// drop one level deeper if necessary
if (current.getFirstChild() != null && !isPruned(current.getFirstChild())) {
return contains(current.getFirstChild(), possible);
}
// already checked subtree, eliminate
addPruned(current);
current = current.getNextSibling();
}
// never found it
return false;
}
private Node getPossibleParent() {
return fPossibleParent;
}
public Node getSmallestParent() {
return fSmallestParent;
}
private StructuredViewer getStructuredViewer() {
return fStructuredViewer;
}
/**
* @return true if this job is the last RefreshOutlineJob running
*/
private boolean iAmLast() {
IJobManager jobManager = Platform.getJobManager();
Job[] jobs = jobManager.find(null);
Job job = null;
for (int i = 0; i < jobs.length; i++) {
job = jobs[i];
if (job instanceof RefreshOutlineJob || job instanceof RefreshOutlineJob.RefreshJob) {
int state = job.getState();
if (state == Job.RUNNING || state == Job.WAITING) {
if (jobs[i] != this) {
if (DEBUG)
System.out.println("... still waiting for another refresh job: " + jobs[i]);
return false;
}
}
}
}
return true;
}
private boolean isPruned(Node n) {
Iterator it = fPruned.iterator();
while (it.hasNext()) {
if (it.next().equals(n))
return true;
}
return false;
}
/**
* perform on UI Thread
*/
public void refreshViewer() {
Job refresh = new RefreshJob(getStructuredViewer());
refresh.setPriority(Job.SHORT);
refresh.setSystem(true);
refresh.schedule();
}
/**
* perform on worker thread
*/
public IStatus run(IProgressMonitor monitor) {
IStatus result = Status.OK_STATUS;
// monitor.beginTask("", IProgressMonitor.UNKNOWN);
try {
calculateSmallestCommonParent();
// only the last one should update the outline
// in case many requests came in consecutively
if (iAmLast()) {
refreshViewer();
}
} catch (Exception e) {
result = new Status(IStatus.ERROR, EditorPlugin.ID, IStatus.ERROR, "Error updating outline", e);
} finally {
monitor.done();
}
return result;
}
private void setPossibleParent(Node possibleParent) {
fPossibleParent = possibleParent;
}
private void setSmallestParent(Node newParent) {
fSmallestParent = newParent;
}
/**
* @param structuredViewer
*/
private void setStructuredViewer(StructuredViewer structuredViewer) {
fStructuredViewer = structuredViewer;
}
}