
com.extjs.gxt.ui.client.store.TreeStore Maven / Gradle / Ivy
/*
* Ext GWT - Ext for GWT
* Copyright(c) 2007-2009, Ext JS, LLC.
* [email protected]
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.store;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.data.ChangeEvent;
import com.extjs.gxt.ui.client.data.ChangeEventSource;
import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.SortInfo;
import com.extjs.gxt.ui.client.data.TreeLoadEvent;
import com.extjs.gxt.ui.client.data.TreeLoader;
import com.extjs.gxt.ui.client.data.TreeModel;
import com.extjs.gxt.ui.client.event.LoadListener;
import com.extjs.gxt.ui.client.util.Util;
/**
* A store for hierarchical data.
*
*
* The parent child relationships are handled internally by the store. It is
* important to note that the store does not use the the parent and children of
* any TreeModel instances added to the store.
*
*
* It is important to note the sorting behavior when working with TreeStore.
* When a sorter is set, it is applied to all existing models in the cache and
* the Sort event is fired. At this point, the sorter is enabled and active.
*
*
* Remote sorting is not supported with TreeStore.
*
* Events:
*
* Store.BeforeDataChanged : TreeStoreEvent(store)
* Fires before the store's data is changed. Apply applies when a store is
* bound to a loader.
*
* - store : this
*
*
*
* Store.DataChange : TreeStoreEvent(store)
* Fires when the data cache has changed, and a widget which is using this
* Store as a Model cache should refresh its view.
*
* - store : this
*
*
*
* Store.Filter : TreeStoreEvent(store)
* Fires when filters are applied and removed from the store.
*
* - store : this
*
*
*
* Store.Add : TreeStoreEvent(store, parent, child)
* Fires when models have been added to the store.
*
* - store : this
* - parent : the parent
* - children : this list of model(s) added
* - index : the insert index
*
*
*
* Store.Remove : TreeStoreEvent(store, parent, child)
* Fires when a model has been removed from the store.
*
* - store : this
* - parent : the parent
* - child : the removed child
* - children : all the children (deep) of the removed item
*
*
*
* Store.Update : TreeStoreEvent(store, model, record)
* Fires when a model has been updated via its record.
*
* - store : this
* - model : the model that was updated
* - record : the record that was updated
* - operation : the update operation being performed.
*
*
*
* Store.Clear : TreeStoreEvent(store)
* Fires when the data cache has been cleared.
*
* - store : this
*
*
*
* Store.Sort : TreeStoreEvent(store)
* Fires after a sorter is applied to the store.
*
* - store : this
*
*
*
*
*/
public class TreeStore extends Store {
protected Map modelMap = new HashMap();
protected Map wrapperMap = new HashMap();
protected BaseTreeModel rootWrapper = new BaseTreeModel();
protected TreeLoader loader;
/**
* Creates a new tree store.
*/
public TreeStore() {
super();
}
/**
* Creates a new tree store.
*
* @param loader the tree loader
*/
public TreeStore(TreeLoader loader) {
this.loader = loader;
loader.addLoadListener(new LoadListener() {
public void loaderBeforeLoad(LoadEvent le) {
onBeforeLoad(le);
}
public void loaderLoad(LoadEvent le) {
onLoad((TreeLoadEvent) le);
}
public void loaderLoadException(LoadEvent le) {
onLoadException(le);
}
});
}
/**
* Adds the models to the root of the store and fires the Add event.
*
* @param models the models to be added
* @param addChildren true to recursively add all children
*/
public void add(List models, boolean addChildren) {
insert(models, rootWrapper.getChildCount(), addChildren);
}
/**
* Adds the items to the store and fires the Add event.
*
* @param item the item to add
* @param addChildren true to recursively add all children
*/
@SuppressWarnings("unchecked")
public void add(M item, boolean addChildren) {
add(Util.createList(item), addChildren);
}
/**
* Adds the models to the given parent and fires the Add event.
*
* @param parent the parent
* @param children the children
* @param addChildren true to recursively add all children
*/
public void add(M parent, List children, boolean addChildren) {
insert(parent, children, getChildCount(parent), addChildren);
}
/**
* Adds the child to the parent and fires the Add event.
*
* @param parent the parent item
* @param item the child item
* @param addChildren true to recursively add all children
*/
public void add(M parent, M item, boolean addChildren) {
insert(parent, item, getChildCount(parent), addChildren);
}
@Override
public void applyFilters(String property) {
if (filters != null && filters.size() == 0) {
return;
}
filterProperty = property;
filtersEnabled = true;
filterTreeWrap(rootWrapper);
fireEvent(Filter, createStoreEvent());
}
@Override
public void clearFilters() {
if (isFiltered()) {
filtersEnabled = false;
snapshot = null;
for (M m : all) {
TreeModel wrap = findWrapper(m);
wrap.remove("filtered");
}
fireEvent(Filter, createStoreEvent());
}
}
/**
* Returns all the stores items. The items are not returned in any order.
*
* @return the items
*/
public List getAllItems() {
return all;
}
/**
* Returns the root level child.
*
* @param index the index
* @return the child
*/
public M getChild(int index) {
return getFilteredChildren(rootWrapper).get(index);
}
/**
* Returns the child at the given index.
*
* @param parent the parent model or null if parent is root
* @param index the index
* @return the child of the parent at the given index
*/
public M getChild(M parent, int index) {
if (parent == null) {
return getFilteredChildren(rootWrapper).get(index);
}
TreeModel p = findWrapper(parent);
if (p != null) {
return getFilteredChildren(p).get(index);
}
return null;
}
/**
* Returns the root level child count.
*
* @return the child count
*/
public int getChildCount() {
return getRootItems().size();
}
/**
* Returns the child count for the parent.
*
* @param parent the parent
* @return the child count or -1 if parent not found in the store
*/
public int getChildCount(M parent) {
if (parent == null) {
return getChildCount();
} else {
TreeModel p = findWrapper(parent);
if (p != null) {
return getFilteredChildren(p).size();
}
return -1;
}
}
/**
* Returns the children of the parent.
*
* @param parent the parent
* @return the children or null if parent not found in the store
*/
public List getChildren(M parent) {
return getChildren(parent, false);
}
/**
* Returns the children of the parent.
*
* @param parent the parent
* @param deep true to return all children recursively
* @return the children or null if parent not found in the store
*/
public List getChildren(M parent, boolean deep) {
TreeModel p = findWrapper(parent);
if (p != null) {
if (deep) {
List temp = new ArrayList();
List children = getFilteredChildren(p);
for (M child : children) {
temp.add(child);
temp.addAll(getChildren(child, true));
}
return temp;
} else {
return getFilteredChildren(p);
}
}
return null;
}
/**
* Returns the depth of the item.
*
* @param item the item
* @return the item's depth
*/
public int getDepth(M item) {
int depth = 0;
while (item != null) {
depth++;
item = getParent(item);
}
return depth;
}
/**
* Returns the fist child of the parent.
*
* @param parent the parent
* @return the first child or null if no children
*/
public M getFirstChild(M parent) {
if (parent == null) {
if (getRootItems().size() > 0) {
return getRootItems().get(0);
}
} else {
if (getChildCount(parent) > 0) {
return getChild(parent, 0);
}
}
return null;
}
/**
* Returns the last child of the parent.
*
* @param parent the parent
* @return the last child of null if no children
*/
public M getLastChild(M parent) {
List children = parent == null ? getRootItems() : getChildren(parent);
if (children.size() > 0) {
return children.get(children.size() - 1);
}
return null;
}
/**
* Returns the store's loader.
*
* @return the loader
*/
public TreeLoader getLoader() {
return loader;
}
/**
* Returns the parent-child relationships for the given model. The actual
* model can be retrieved in each TreeModel's "model" property using the
* {@link TreeStoreModel#getModel()} method. The children of each tree model
* contains tree model instances which wrap the actual child model.
*
* @param model the model
* @return the model and it's children
*/
public TreeStoreModel getModelState(M model) {
TreeStoreModel tm = new TreeStoreModel(model);
int count = getChildCount(model);
for (int i = 0; i < count; i++) {
tm.add(getModelState(getChild(model, i)));
}
return tm;
}
/**
* Returns the next sibling of the model.
*
* @param item the model
* @return the next sibling or null
*/
public M getNextSibling(M item) {
M parent = getParent(item);
List children = parent == null ? getRootItems() : getChildren(parent);
int index = children.indexOf(item);
if (children.size() > (index + 1)) {
return children.get(index + 1);
}
return null;
}
/**
* Returns the parent of the item.
*
* @param item the item
* @return the item's parent
*/
public M getParent(M item) {
TreeModel child = findWrapper(item);
if (child != null) {
TreeModel p = child.getParent();
if (p != null) {
return unwrap(p);
}
}
return null;
}
/**
* Returns the item's previous sibling.
*
* @param item the item
* @return the previous sibling
*/
public M getPreviousSibling(M item) {
M parent = getParent(item);
List children = parent == null ? getRootItems() : getChildren(parent);
int index = children.indexOf(item);
if (index > 0) {
return children.get(index - 1);
}
return null;
}
/**
* Returns the root level items.
*
* @return the items
*/
public List getRootItems() {
return unwrapChildren(rootWrapper);
}
/**
* Returns the current sort state of this store.
*
* @return the sort state
*/
public SortInfo getSortState() {
return sortInfo;
}
/**
* Returns the item's index in it's parent including root level items.
*
* @param item the item
* @return the index
*/
public int indexOf(M item) {
M parent = getParent(item);
if (parent == null) {
return getRootItems().indexOf(item);
} else {
return getChildren(parent).indexOf(item);
}
}
/**
* Inserts the models into the store and fires the Add event.
*
* @param models the models to insert
* @param index the insert index
* @param addChildren true to recursively add all children
*/
public void insert(List models, int index, boolean addChildren) {
List insert = new ArrayList();
for (M model : models) {
insert.add(wrap(model));
}
doInsert(rootWrapper, insert, index, addChildren, false);
}
/**
* Adds the item to the store and fires the Add event.
*
* @param item the item to insert
* @param index the insert index
* @param addChildren true to recursively add all children
*/
@SuppressWarnings("unchecked")
public void insert(M item, int index, boolean addChildren) {
doInsert(rootWrapper, Util.createList(wrap(item)), index, addChildren, false);
}
/**
* Inserts the children to the parent and fires the Add event.
*
* @param parent the parent
* @param children the children
* @param index the insert index
* @param addChildren true to recursively add all children
*/
public void insert(M parent, List children, int index, boolean addChildren) {
TreeModel wrapper = findWrapper(parent);
if (wrapper != null) {
List insert = new ArrayList();
for (M model : children) {
insert.add(wrap(model));
}
doInsert(wrapper, insert, index, addChildren, false);
}
}
/**
* Adds the child to the parent and fires the Add event.
*
* @param parent the parent model
* @param model the child model
* @param index the insert index
* @param addChildren true to recursively add all children
*/
@SuppressWarnings("unchecked")
public void insert(M parent, M model, int index, boolean addChildren) {
insert(parent, Util.createList(model), index, addChildren);
}
/**
* Removes the model from the store and fires the Remove event. Works
* with both regular and root nodes.
*
* @param model the item to be removed
*/
public void remove(M model) {
M parent = getParent(model);
if (parent == null) {
remove(rootWrapper, findWrapper(model), false);
} else {
remove(parent, model);
}
}
/**
* Removes the child from the parent and fires the Remove event.
*
* @param parent the parent model
* @param child the child model
*/
public void remove(M parent, M child) {
TreeModel p = findWrapper(parent);
TreeModel c = findWrapper(child);
if (p != null && c != null) {
remove(p, c, false);
}
}
@Override
public void removeAll() {
removeAll(false);
}
/**
* Removes all the parent's children.
*
* @param parent the parent
*/
public void removeAll(M parent) {
TreeModel p = findWrapper(parent);
if (p != null) {
List children = getChildren(parent);
for (M m : children) {
TreeModel child = findWrapper(m);
if (child != null) {
remove(p, child, false);
}
}
}
}
/**
* Sets the current sort info used when sorting items in the store.
*
* @param info the sort info
*/
public void setSortInfo(SortInfo info) {
this.sortInfo = info;
}
@Override
public void setStoreSorter(StoreSorter storeSorter) {
super.setStoreSorter(storeSorter);
applySort(false);
}
/**
* Sorts the store.
*
* @param field the field to sort by
* @param sortDir the sort dir
*/
public void sort(String field, SortDir sortDir) {
if (sortDir == null) {
if (sortInfo.getSortField() != null && !sortInfo.getSortField().equals(field)) {
sortInfo.setSortDir(SortDir.NONE);
}
switch (sortInfo.getSortDir()) {
case ASC:
sortDir = SortDir.DESC;
break;
case DESC:
case NONE:
sortDir = SortDir.ASC;
break;
}
}
sortInfo.setSortField(field);
sortInfo.setSortDir(sortDir);
applySort(false);
fireEvent(DataChanged, createStoreEvent());
}
@Override
@SuppressWarnings("unchecked")
protected void applySort(boolean supressEvent) {
for (TreeModel wrapper : wrapperMap.keySet()) {
List children = (List) rootWrapper.getChildren();
if (children.size() > 0) {
applySort(children);
}
children = (List) wrapper.getChildren();
if (children.size() > 0) {
applySort(children);
}
}
if (!supressEvent) {
fireEvent(Sort, createStoreEvent());
}
}
@SuppressWarnings("unchecked")
protected void applySort(List list) {
storeSorter = storeSorter == null ? new StoreSorter() : storeSorter;
Collections.sort(list, new Comparator() {
public int compare(TreeModel m1, TreeModel m2) {
return storeSorter.compare(TreeStore.this, unwrap(m1), unwrap(m2), sortInfo.getSortField());
}
});
if (sortInfo.getSortDir() == SortDir.DESC) {
Collections.reverse(list);
}
}
@Override
protected TreeStoreEvent createStoreEvent() {
return new TreeStoreEvent(this);
}
protected TreeModel findWrapper(M item) {
return modelMap.get(item);
}
protected void onBeforeLoad(LoadEvent le) {
fireEvent(BeforeDataChanged, createStoreEvent());
}
@SuppressWarnings("unchecked")
protected void onLoad(TreeLoadEvent le) {
if (le.parent == null) {
removeAll(true);
List insert = new ArrayList();
for (M model : (List) le.getData()) {
insert.add(wrap(model));
}
doInsert(rootWrapper, insert, 0, false, true);
fireEvent(DataChanged, new TreeStoreEvent(this));
} else {
TreeModel wrapper = findWrapper((M) le.parent);
if (wrapper != null) {
if (wrapper.getChildren().size() > 0) {
removeAll((M) le.parent);
}
List insert = new ArrayList();
List list = (List) le.getData();
for (M model : list) {
insert.add(wrap(model));
}
doInsert(wrapper, insert, 0, false, true);
TreeStoreEvent evt = new TreeStoreEvent(this);
evt.setParent(le.parent);
evt.setChildren(unwrapChildren(wrapper));
fireEvent(DataChanged, evt);
}
}
}
protected void onLoadException(LoadEvent le) {
}
@SuppressWarnings("unchecked")
@Override
protected void onModelChange(ChangeEvent ce) {
super.onModelChange(ce);
switch (ce.getType()) {
case ChangeEventSource.Add: {
if (ce.getSource() == ce.getParent()) {
M parent = (M) ce.getParent();
M child = (M) ce.getItem();
insert(parent, child, ce.getIndex(), false);
}
break;
}
case ChangeEventSource.Remove: {
if (ce.getSource() == ce.getParent()) {
M parent = (M) ce.getParent();
M child = (M) ce.getItem();
remove(parent, child);
}
break;
}
}
}
protected List unwrap(List models) {
List children = new ArrayList();
if (isFiltered()) {
for (TreeModel child : models) {
if (!"true".equals(child.get("filtered"))) {
children.add(unwrap(child));
}
}
} else {
for (TreeModel child : models) {
children.add(unwrap(child));
}
}
return children;
}
protected M unwrap(TreeModel wrapper) {
return wrapperMap.get(wrapper);
}
@SuppressWarnings("unchecked")
protected List unwrapChildren(TreeModel parent) {
return unwrap((List) parent.getChildren());
}
protected TreeModel wrap(M model) {
TreeModel wrapper = new BaseTreeModel();
modelMap.put(model, wrapper);
wrapperMap.put(wrapper, model);
return wrapper;
}
@SuppressWarnings("unchecked")
private void doInsert(TreeModel parent, List children, int index, boolean addChildren, boolean supressEvent) {
if (parent != null && children != null && children.size() > 0) {
M modelParent = unwrap(parent);
for (int i = children.size() - 1; i >= 0; i--) {
parent.insert(children.get(i), index);
M m = unwrap(children.get(i));
all.add(m);
registerModel(m);
}
if (storeSorter != null) {
applySort((List) parent.getChildren());
}
if (!supressEvent) {
if (storeSorter != null) {
for (TreeModel child : children) {
TreeStoreEvent evt = createStoreEvent();;
evt.setParent(modelParent);
evt.setIndex(parent.indexOf(child));
evt.setChildren(Util.createList(unwrap(child)));
fireEvent(Add, evt);
}
} else {
TreeStoreEvent evt = createStoreEvent();;
evt.setParent(modelParent);
evt.setChildren(unwrap(children));
evt.setIndex(index);
fireEvent(Add, evt);
}
}
if (addChildren) {
for (TreeModel sub : children) {
M model = unwrap(sub);
if (model instanceof TreeModel) {
TreeModel treeSub = (TreeModel) model;
List insert = new ArrayList();
List c = (List) treeSub.getChildren();
for (M m : c) {
insert.add(wrap(m));
}
doInsert(sub, insert, getChildCount(model), true, false);
update(model);
}
}
}
}
}
private void filterTreeWrap(TreeModel wrap) {
List children = wrap.getChildren();
for (int i = 0, len = children.size(); i < len; i++) {
TreeModel tm = (TreeModel) children.get(i);
if (isOrDecendantSelected(tm, unwrap(tm))) {
tm.set("filtered", "false");
} else {
tm.set("filtered", "true");
}
filterTreeWrap(tm);
}
}
private void findChildren(M parent, List children) {
for (M child : getChildren(parent)) {
children.add(child);
findChildren(child, children);
}
}
private List getFilteredChildren(TreeModel wrap) {
List filtered = new ArrayList();
for (ModelData child : wrap.getChildren()) {
if (!isFiltered((TreeModel) child)) {
filtered.add((TreeModel) child);
}
}
return unwrap(filtered);
}
private boolean isFiltered(TreeModel wrap) {
return "true".equals(wrap.get("filtered"));
}
private boolean isOrDecendantSelected(TreeModel wrap, M model) {
if (!isFiltered(model, filterProperty)) {
return true;
}
List children = wrap.getChildren();
for (int i = 0, len = children.size(); i < len; i++) {
TreeModel tm = (TreeModel) children.get(i);
boolean result = isOrDecendantSelected(tm, unwrap(tm));
if (result) {
return true;
}
}
return false;
}
private void remove(TreeModel parent, TreeModel child, boolean supressEvent) {
int index = parent.getChildren().indexOf(child);
if (index != -1) {
parent.remove(child);
M model = unwrap(child);
List children = new ArrayList();
findChildren(model, children);
for (M c : children) {
all.remove(c);
modelMap.remove(c);
unregisterModel(c);
wrapperMap.remove(findWrapper(c));
}
all.remove(model);
wrapperMap.remove(child);
modelMap.remove(model);
unregisterModel(model);
if (!supressEvent) {
TreeStoreEvent evt = new TreeStoreEvent(this);
evt.setParent(unwrap(parent));
evt.setChild(model);
evt.setChildren(children);
evt.setIndex(index);
fireEvent(Remove, evt);
}
}
}
private void removeAll(boolean supressEvent) {
all.clear();
modified.clear();
modelMap.clear();
wrapperMap.clear();
rootWrapper.removeAll();
if (!supressEvent) {
fireEvent(Clear, createStoreEvent());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy