blob: b4e3f3ec9cdf6696e481de2079dc620d0bb52da0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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
*******************************************************************************/
package org.eclipse.jface.internal.databinding.viewers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.databinding.ChangeEvent;
import org.eclipse.jface.databinding.IUpdatableTree;
import org.eclipse.jface.databinding.Updatable;
import org.eclipse.jface.internal.databinding.swt.AsyncRunnable;
import org.eclipse.jface.internal.databinding.swt.SyncRunnable;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
/**
* @since 3.2
*
*/
//TODO Thread safe
public class TreeViewerUpdatableTree extends Updatable implements IUpdatableTree {
protected TreeViewer viewer;
// TODO use identityWrapper
private Map nodes = new HashMap();
private Object rootKey=this;
private Object viewerInput=null;
private boolean updatingViewer=false;
private boolean updatingVirtual=false;
private List viewerRefreshList = new ArrayList();
Class[] classTypes;
private class TreeNode {
Object parent;
List children;
boolean requestedChildren=false;
private TreeNode(Object parent, List children) {
this.parent=parent;
this.children=children;
}
private TreeNode(Object parent) {
this(parent, null);
}
/**
* @return a List, the children of this node
*/
public List getChildren() {
return children;
}
/**
* @return an Object, the parent of this Node
*/
public Object getParent() {
return parent;
}
/**
* @param children
*/
public void setChildren(List children) {
this.children = children;
requestedChildren=false;
}
/**
* @return true if VIRTUAL request has been made already
*/
public boolean isRequestedChildren() {
return requestedChildren;
}
/**
* @param requestedChildren
*/
public void setRequestedChildren(boolean requestedChildren) {
this.requestedChildren = requestedChildren;
}
}
/**
* @param viewer
*/
public TreeViewerUpdatableTree(final TreeViewer viewer) {
this (viewer, new Class[] { Object.class } );
}
/**
* @param viewer
* @param classTypes
*/
public TreeViewerUpdatableTree(final TreeViewer viewer, Class[] classTypes) {
this.viewer = viewer;
this.classTypes=classTypes;
viewer.setContentProvider(new ITreeContentProvider() {
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
viewerInput=newInput;
}
public void dispose() {}
public Object[] getElements(Object inputElement) {
return TreeViewerUpdatableTree.this.getElements(inputElement);
}
public boolean hasChildren(final Object element) {
return TreeViewerUpdatableTree.this.hasChildren(element);
}
public Object getParent(Object element) {
TreeNode node = getTreeNode(element);
if (node!=null)
return node.getParent();
return null;
}
public Object[] getChildren(Object parentElement) {
return TreeViewerUpdatableTree.this.getElements(parentElement);
}
});
this.viewer.setInput(this);
}
protected TreeNode getTreeNode(Object element) {
if (element==null || element==rootKey || element==viewerInput) {
TreeNode result = (TreeNode) nodes.get(rootKey);
if (result==null) {
result = new TreeNode(null);
nodes.put(rootKey, result);
}
return result;
}
return (TreeNode)nodes.get(element);
}
private int primAddChildElement(List list, Object element, int index) {
int position;
if (index < 0 || index > list.size()) {
position = list.size();
list.add(element);
} else {
list.add(index, element);
position = index;
}
return position;
}
protected void updateViewer(Runnable runable) {
if (updatingViewer) {
viewerRefreshList.add(runable);
return ;
}
updatingViewer=true;
try {
runable.run();
}
finally {
updatingViewer=false;
}
// Virtual events may need to update the viewer as well
while (!viewerRefreshList.isEmpty()) {
Runnable work[] = (Runnable[])viewerRefreshList.toArray(new Runnable[viewerRefreshList.size()]);
viewerRefreshList.clear();
for (int i = 0; i < work.length; i++)
updateViewer(work[i]);
}
}
public int addElement(final Object parentElement, final int index, final Object value) {
SyncRunnable runnable = new SyncRunnable() {
public Object run() {
return new Integer(addElement(parentElement, index, value, true));
}
};
return ((Integer)runnable.run()).intValue();
}
protected int addElement(final Object parentElement, int index, final Object value, final boolean fire) {
TreeNode parentNode = getTreeNode(parentElement);
if (parentNode == null) return -1;
if (!hasChildren(parentElement) && parentNode.getChildren()==null)
parentNode.setChildren(new ArrayList());
List list = parentNode.getChildren();
int addedIndex = -1;
if (!list.contains(value)) {
addedIndex = primAddChildElement(list, value, index);
TreeNode valueNode = new TreeNode(parentElement);
nodes.put(value, valueNode);
final int fireIndex=addedIndex;
updateViewer(new Runnable() {
public void run() {
viewer.add(parentElement == null ? viewerInput : parentElement,
value);
if (fire)
fireChangeEvent(ChangeEvent.ADD, null, value, parentElement, fireIndex);
}
});
}
return addedIndex;
}
public void removeElement(final Object parentElement, final int index) {
SyncRunnable runnable = new SyncRunnable() {
public Object run() {
removeElement(parentElement, index, true);
return null;
}
};
runnable.run();
}
protected void removeElement(final Object parentElement, final int index, final boolean fire) {
if (hasChildren(parentElement)) {
TreeNode parentNode = getTreeNode(parentElement);
List children = parentNode.getChildren();
if (children!=null) {
final Object element = children.remove(index);
updateViewer(new Runnable() {
public void run() {
viewer.remove(element);
if (fire)
fireChangeEvent(ChangeEvent.REMOVE, element, null, parentElement, index);
}
});
}
if (children.size()==0)
parentNode.setChildren(null);
}
}
public void setElement(final Object parentElement, final int index, final Object value) {
AsyncRunnable runnable = new AsyncRunnable() {
public void run() {
if (hasChildren(parentElement)) {
TreeNode parentNode = getTreeNode(parentElement);
List children = parentNode.getChildren();
Object oldValue = children.get(index);
if (oldValue.equals(value)) {
updateViewer(new Runnable() {
public void run() {
viewer.update(value, null);
}
});
} else {
removeElement(parentElement, index, false);
addElement(parentElement, index, value, false);
}
fireChangeEvent(ChangeEvent.CHANGE, oldValue, value,
parentElement, index);
}
}
};
runnable.runOn(viewer.getTree().getDisplay());
}
public void setElements(final Object parentElement, final Object[] values) {
SyncRunnable runnable = new SyncRunnable() {
public Object run() {
hasChildren(parentElement); // prime Children if needed
TreeNode parentNode = getTreeNode(parentElement);
if (parentNode == null)
return null;
List children = parentNode.getChildren();
if (children != null) {
while (children.size() > 0)
removeElement(parentElement, 0, true);
}
parentNode.setRequestedChildren(true);
if (values != null)
for (int i = 0; i < values.length; i++) {
addElement(parentElement, i, values[i], true);
}
return null;
}
};
runnable.runOn(viewer.getTree().getDisplay());
}
public Object getElement(Object parentElement, int index) {
Object element = null;
if (hasChildren(parentElement)) {
TreeNode parentNode = getTreeNode(parentElement);
if (parentNode!=null) {
List children = parentNode.getChildren();
if (children!=null)
if (index>=0&&index<children.size())
element = children.get(index);
}
}
return element;
}
public Object[] getElements(Object parentElement) {
if (hasChildren(parentElement)) {
TreeNode parentNode = getTreeNode(parentElement);
List children = parentNode==null? null : parentNode.getChildren();
return children==null? Collections.EMPTY_LIST.toArray():children.toArray();
}
return Collections.EMPTY_LIST.toArray();
}
/**
* This method will drive <code>ChangeElement.VIRTUAL</code> if no
* childrent are set.
*
* @param element
* @return true if element has children, false if not
*/
public boolean hasChildren(final Object element) {
TreeNode node = getTreeNode(element);
if (node==null) return false;
boolean have = node.getChildren()!=null && node.getChildren().size()>0;
if (!have && !updatingVirtual && !node.isRequestedChildren()) {
updatingVirtual=true;
try {
fireChangeEvent(ChangeEvent.VIRTUAL, null, null, element, -1);
// Once asked, the assumption is that the model will listen, and update
// if a new child is added
node.setRequestedChildren(true);
// VIRTUAL processing may or may not be on the same thread
// In any case, the binder will come back with the right answer
// eventually
have = node.getChildren()!=null && node.getChildren().size()>0;
}
finally {
updatingVirtual=false;
}
}
return have;
}
/**
* @param element
* @return element's parent
*/
public Object getParent(Object element) {
TreeNode elementNode = (TreeNode)nodes.get(element);
return elementNode.getParent();
}
public Class[] getTypes() {
return classTypes;
}
}