blob: e5e022e2e3f18af2c675f298749f0143a16cde03 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems 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.debug.internal.ui.viewers.model;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.core.runtime.Assert;
/**
* Virtual item, which is analogous to the SWT's tree item.
*
* @since 3.5
*/
class VirtualItem {
// Data keys for display attributes of an item.
static String LABEL_KEY = "LABEL_KEY"; //$NON-NLS-1$
static String IMAGE_KEY = "IMAGE_KEY"; //$NON-NLS-1$
static String FONT_KEY = "FONT_KEY"; //$NON-NLS-1$
static String FOREGROUND_KEY = "FOREGROUND_KEY"; //$NON-NLS-1$
static String BACKGROUND_KEY = "BACKGROUND_KEY"; //$NON-NLS-1$
static String ELEMENT_DATA_KEY = "element"; //$NON-NLS-1$
/**
* Index object of a tree item. It allows the indexes to be modified
* as items are inserted and removed.
*/
public static class Index implements Comparable {
private Integer fIndexValue;
public Index(int index) {
fIndexValue = new Integer(index);
}
public boolean equals(Object obj) {
return obj instanceof Index && ((Index)obj).fIndexValue.equals(fIndexValue);
}
public int hashCode() {
return fIndexValue.hashCode();
}
public void increment() {
fIndexValue = new Integer(fIndexValue.intValue() + 1);
}
public void decrement() {
fIndexValue = new Integer(fIndexValue.intValue() - 1);
}
public int intValue() {
return fIndexValue.intValue();
}
public int compareTo(Object obj) {
return obj instanceof Index ? fIndexValue.compareTo(((Index)obj).fIndexValue) : 0;
}
public String toString() {
return fIndexValue.toString();
}
}
/**
* Parent items of this item.
*/
final private VirtualItem fParent;
/**
* The index of this item.
*/
final private Index fIndex;
/**
* Map of child items. The key to the map is the item's index, which
* must be the same object instance as the index in the item. The tree map
* keeps the items sorted while allowing indexes (keys) to be modified as
* child items are inserted and removed.
*/
private Map fItems = new TreeMap();
/**
* Flag indicating whether this item has child items.
*/
private boolean fHasItems = false;
/**
* Indicates that this item has been expanded. It should only
* be set to <code>true</code> if fHasItems is <code>true</code>.
*/
private boolean fExpanded = false;
/**
* The cound of child items. <code>-1</code> indicates that the count
* is not known.
*/
private int fItemCount = -1;
/**
* The data held by this item. It includes the element as well as the item
* display attributes.
*/
private Map fData = new HashMap(1);
/**
* Flag indicating that the item needs to have it's label updated.
*/
private boolean fNeedsLabelUpdate = true;
/**
* Flag indicating that the item's count needs to be updated.
*/
private boolean fNeedsCountUpdate = true;
/**
* Flag indicating that the item's element needs to be updated.
*/
private boolean fNeedsDataUpdate = true;
/**
* Indicates that this item has been disposed.
*/
private boolean fDisposed = false;
VirtualItem(VirtualItem parent, Index index) {
fParent = parent;
fIndex = index;
}
void setNeedsCountUpdate() {
fNeedsCountUpdate = true;
fItemCount = -1;
}
void setNeedsLabelUpdate() {
fNeedsLabelUpdate = true;
}
void setNeedsDataUpdate() {
fNeedsDataUpdate = true;
}
void clear(Index index) {
VirtualItem item = (VirtualItem)fItems.remove(index);
if (item != null) {
item.dispose();
}
}
VirtualItem getParent() {
return fParent;
}
Index getIndex() {
return fIndex;
}
VirtualItem findItem(Object element) {
for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
VirtualItem next = (VirtualItem)itr.next();
Object nextData = next.getData();
if ( (element != null && element.equals(nextData)) || (element == null && nextData == null) ) {
return next;
}
}
return null;
}
boolean needsDataUpdate() {
return fNeedsDataUpdate;
}
void clearNeedsDataUpdate() {
fNeedsDataUpdate = false;
}
boolean needsCountUpdate() {
return fNeedsCountUpdate;
}
void clearNeedsCountUpdate() {
fNeedsCountUpdate = false;
}
boolean needsLabelUpdate() {
return fNeedsLabelUpdate;
}
void clearNeedsLabelUpdate() {
fNeedsLabelUpdate = false;
}
boolean isDisposed() {
return fDisposed;
}
void dispose() {
fData.clear();
for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
((VirtualItem)itr.next()).dispose();
}
fItems.clear();
fDisposed = true;
findTree().fireItemDisposed(this);
}
Object getData (String key) {
return fData.get(key);
}
void setData(String key, Object data) {
fData.put(key, data);
}
void setData(Object data) {
fData.put(ELEMENT_DATA_KEY, data);
}
Object getData () {
return fData.get(ELEMENT_DATA_KEY);
}
void setExpanded(boolean expanded) {
if (fExpanded == expanded) {
return;
}
fExpanded = expanded;
if (fExpanded && getItemCount() == -1) {
setNeedsCountUpdate();
}
Assert.isTrue(!fExpanded || hasItems());
// If collapsed, make sure that all the children are collapsed as well.
if (!fExpanded) {
for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
((VirtualItem)itr.next()).setExpanded(expanded);
}
}
}
boolean getExpanded() {
return fExpanded;
}
void setHasItems(boolean hasChildren) {
fHasItems = hasChildren;
if (!fHasItems) {
if (getItemCount() != 0) {
setItemCount(0);
}
} else if (getItemCount() == 0) {
setItemCount(-1);
}
}
boolean hasItems() {
return fHasItems;
}
void setItemCount(int count) {
fItemCount = count;
for (Iterator itr = fItems.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry)itr.next();
int index = ((Index)entry.getKey()).intValue();
if (index >= count) {
itr.remove();
VirtualItem item = (VirtualItem)entry.getValue();
item.dispose();
}
}
if (fItemCount == 0) {
if (hasItems()) {
setHasItems(false);
}
if (getExpanded()) {
setExpanded(false);
}
} else {
if (!hasItems()) {
setHasItems(true);
}
}
}
int getItemCount() {
return fItemCount;
}
VirtualItem getItem(Index index) {
ensureItems();
VirtualItem item = (VirtualItem)fItems.get(index);
if (item == null) {
item = new VirtualItem(this, index);
fItems.put(index, item);
}
return item;
}
boolean childrenNeedDataUpdate() {
if (getItemCount() == 0) {
return false;
}
if (fItems == null || fItems.size() != fItemCount) {
return true;
}
for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
VirtualItem child = (VirtualItem)itr.next();
if (child.needsDataUpdate()) {
return true;
}
}
return false;
}
VirtualItem[] getItems() {
return (VirtualItem[]) fItems.values().toArray(new VirtualItem[fItems.size()]);
}
VirtualItem addItem(int position) {
if (!fHasItems) {
fHasItems = true;
}
if (fItemCount < 0) {
fItemCount = 0;
}
// Increment all items with an index higher than the given position.
fItemCount++;
ensureItems();
for (Iterator itr = fItems.keySet().iterator(); itr.hasNext();) {
Index childIndex = (Index)itr.next();
if (childIndex.intValue() >= position) {
childIndex.increment();
}
}
// Note: the same index object used to create the item has to
// be used as the key into the map.
Index childIndex = new Index(position);
VirtualItem newChild = new VirtualItem(this, childIndex);
fItems.put(childIndex, newChild);
return newChild;
}
void remove(Index position) {
fItemCount--;
if (fItemCount < 0) {
fHasItems = false;
}
ensureItems();
VirtualItem removedItem = null;
for (Iterator itr = fItems.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = (Map.Entry)itr.next();
Index childIndex = (Index)entry.getKey();
if (childIndex.intValue() > position.intValue()) {
childIndex.decrement();
} else if (childIndex.intValue() == position.intValue()) {
removedItem = (VirtualItem)entry.getValue();
removedItem.dispose();
itr.remove();
}
}
}
private void ensureItems() {
if (fItems == null) {
fItems = new HashMap( Math.max(1, Math.min(fItemCount, 16)) );
}
}
private VirtualTree findTree() {
VirtualItem item = this;
while (!(item instanceof VirtualTree)) {
item = item.fParent;
}
return (VirtualTree)item;
}
public String toString() {
String[] label = (String[])fData.get(LABEL_KEY);
if (label != null && label.length != 0) {
return label[0];
}
Object data = fData.get(ELEMENT_DATA_KEY);
if (data != null) {
return data.toString();
}
return super.toString();
}
}