/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.eas.grid.processing;
import com.eas.ui.events.CollapsedHandler;
import com.eas.ui.events.ExpandedHandler;
import com.eas.ui.events.HasCollapsedHandlers;
import com.eas.ui.events.HasExpandedHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.view.client.ListDataProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This class is the table front to a treed data.
*
* @author mg
* @param <T>
*/
public class TreeDataProvider<T> extends ListDataProvider<T> implements IndexOfProvider<T>, HasExpandedHandlers<T>, HasCollapsedHandlers<T> {
protected Tree<T> tree;
protected Set<T> expanded = new HashSet<>();
protected Map<T, Integer> indicies = new HashMap<>();
protected final Set<ExpandedHandler<T>> expandedHandlers = new HashSet<>();
protected final Set<CollapsedHandler<T>> collapsedHandlers = new HashSet<>();
protected Runnable onResize;
@Override
public HandlerRegistration addExpandedHandler(final ExpandedHandler<T> aHandler) {
expandedHandlers.add(aHandler);
return new HandlerRegistration() {
@Override
public void removeHandler() {
expandedHandlers.remove(aHandler);
}
};
}
@Override
public HandlerRegistration addCollapsedHandler(final CollapsedHandler<T> aHandler) {
collapsedHandlers.add(aHandler);
return new HandlerRegistration() {
@Override
public void removeHandler() {
collapsedHandlers.remove(aHandler);
}
};
}
protected void expanded(T aElement) {
if (onResize != null)
onResize.run();
for (ExpandedHandler<?> handler : expandedHandlers.toArray(new ExpandedHandler<?>[] {})) {
((ExpandedHandler<T>) handler).expanded(aElement);
}
}
protected void collapsed(T aElement) {
if (onResize != null)
onResize.run();
for (CollapsedHandler<?> handler : collapsedHandlers.toArray(new CollapsedHandler<?>[] {})) {
((CollapsedHandler<T>) handler).collapsed(aElement);
}
}
/**
* Table front constructor. Constructs a lazy tree front (asynchronous
* case).
*
* @param aTreedModel
* - Deep treed model, containing data.
*/
public TreeDataProvider(Tree<T> aTreedModel, Runnable aOnResize) {
super();
onResize = aOnResize;
tree = aTreedModel;
// Let's fill with roots forest
getList().addAll(tree.getChildrenOf(null));
tree.addChangesHandler(new Tree.ChangeHandler<T>() {
@Override
public void removed(T aSubject) {
if (indexOf(aSubject) != -1) {
if (isExpanded(aSubject)) {
collapse(aSubject);
} else {
invalidateFront();
validateFront();
}
if (onResize != null)
onResize.run();
}
}
@Override
public void added(T aSubject) {
T parent = tree.getParentOf(aSubject);
if(parent == null || isExpanded(parent)){
invalidateFront();
validateFront();
if (onResize != null)
onResize.run();
}
}
@Override
public void changed(T aSubject) {
int idx = indexOf(aSubject);
if (idx != -1) {
List<T> targetList = getList();
targetList.set(idx, targetList.get(idx));
}
}
@Override
public void everythingChanged() {
expanded.clear();
invalidateFront();
validateFront();
if (onResize != null)
onResize.run();
}
});
}
protected void invalidateFront() {
getList().clear();
indicies.clear();
}
protected void validateFront() {
assert tree != null;
List<T> targetList = getList();
if (targetList.isEmpty()) {
List<T> children = tree.getChildrenOf(null);
targetList.addAll(children);
int i = 0;
while (i < targetList.size()) {
if (expanded.contains(targetList.get(i))) {
List<T> children1 = tree.getChildrenOf(targetList.get(i));
targetList.addAll(i + 1, children1);
}
++i;
}
for (i = 0; i < targetList.size(); i++) {
indicies.put(targetList.get(i), i);
}
}
}
public int indexOf(T aItem) {
validateFront();
Integer idx = indicies.get(aItem);
return idx != null ? idx.intValue() : -1;
}
@Override
public void rescan() {
indicies.clear();
List<T> targetList = getList();
for (int i = 0; i < targetList.size(); i++) {
indicies.put(targetList.get(i), i);
}
}
/**
* Builds path to specified element if the element belongs to the model.
*
* @param anElement
* Element to build path to.
* @return List<T> of elements comprising the path, excluding root null. So
* for the roots of the forest path will be a list with one element.
*/
public List<T> buildPathTo(T anElement) {
List<T> path = new ArrayList<>();
if (anElement != null) {
T currentParent = anElement;
Set<T> added = new HashSet<>();
path.add(currentParent);
added.add(currentParent);
while (currentParent != null) {
currentParent = getParentOf(currentParent);
if(added.contains(currentParent)){
break;
}
if (currentParent != null) {
path.add(0, currentParent);
added.add(currentParent);
}
}
}
return path;
}
protected T getParentOf(T aElement) {
assert tree != null;
return tree.getParentOf(aElement);
}
public boolean isExpanded(T anElement) {
return expanded.contains(anElement);
}
public void expand(final T anElement) {
List<T> children = tree.getChildrenOf(anElement);
if (!expanded.contains(anElement)) {
if (children != null && !children.isEmpty()) {
expanded.add(anElement);
invalidateFront();
validateFront();
expanded(anElement);
}
}
}
public void collapse(T anElement) {
if (expanded.contains(anElement)) {
expanded.remove(anElement);
invalidateFront();
validateFront();
collapsed(anElement);
}
}
public Tree<T> getTree() {
return tree;
}
}