/*
* TreeTableViewModel.java
* Copyright 2008 (C) Connor Petty <mistercpp2000@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on Feb 11, 2008, 9:04:19 PM
*/
package pcgen.gui2.util.treeview;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.swing.JTree;
import pcgen.facade.util.event.ListEvent;
import pcgen.facade.util.event.ListListener;
import pcgen.facade.util.ListFacade;
import pcgen.facade.util.ListFacades;
import pcgen.gui2.util.table.Row;
import pcgen.gui2.util.treetable.AbstractTreeTableModel;
import pcgen.gui2.util.treetable.SortableTreeTableModel;
import pcgen.gui2.util.treetable.SortableTreeTableNode;
import pcgen.gui2.util.treetable.TreeTableNode;
import pcgen.util.CollectionMaps;
import pcgen.util.ListMap;
import pcgen.util.Logging;
/**
*
* @author Connor Petty <mistercpp2000@gmail.com>
*/
public class TreeViewTableModel<E> extends AbstractTreeTableModel
implements SortableTreeTableModel
{
private final ListListener<E> listListener = new ListListener<E>()
{
@Override
public void elementAdded(ListEvent<E> e)
{
addElement(e.getElement());
}
@Override
public void elementRemoved(ListEvent<E> e)
{
removeElement(e.getElement());
}
@Override
public void elementsChanged(ListEvent<E> e)
{
//Todo: optimize
setElements(ListFacades.wrap(model));
}
@Override
public void elementModified(ListEvent<E> e)
{
// refreshElement(e.getElement());
}
};
private final DataViewColumn namecolumn = new DataViewColumn()
{
@Override
public String getName()
{
return selectedView.getViewName();
}
@Override
public Class<?> getDataClass()
{
return TreeTableNode.class;
}
@Override
public Visibility getVisibility()
{
return Visibility.ALWAYS_VISIBLE;
}
@Override
public boolean isEditable()
{
return true;
}
@Override
public boolean shouldCache()
{
return false;
}
};
protected final Set<E> dataElements = new HashSet<>();
protected List<? extends DataViewColumn> datacolumns;
protected DataView<E> dataview;
protected ListFacade<E> model = null;
protected TreeView<? super E> selectedView = null;
protected TreeViewTableModel()
{
}
public TreeViewTableModel(DataView<E> dataView)
{
this.dataview = dataView;
this.datacolumns = dataview.getDataColumns();
}
public void refreshData()
{
// dataElements.clear();
// dataElements.addAll(ListFacades.wrap(model));
}
public final void setDataModel(ListFacade<E> model)
{
if (this.model != null)
{
this.model.removeListListener(listListener);
}
this.model = model;
model.addListListener(listListener);
setData(ListFacades.wrap(model));
}
private void setData(Collection<E> data)
{
dataElements.clear();
dataElements.addAll(data);
setSelectedTreeView(selectedView);
}
private void setElements(Collection<E> data)
{
Set<E> newData = new HashSet<>(data);
for (E newKey : newData)
{
if (!dataElements.contains(newKey))
{
addElement(newKey);
}
}
Set<E> oldData = new HashSet<>(dataElements);
for (E oldKey : oldData)
{
if (!newData.contains(oldKey))
{
removeElement(oldKey);
}
}
}
private void removeElement(E elem)
{
if (dataElements.contains(elem) && selectedView != null)
{
TreeViewNode rootNode = (TreeViewNode) getRoot();
for (TreeViewPath<? super E> path : selectedView.getPaths(elem))
{
rootNode.removeTreeViewPath(path);
}
dataElements.remove(elem);
}
}
private void addElement(E elem)
{
if (!dataElements.contains(elem) && selectedView != null)
{
dataElements.add(elem);
TreeViewNode rootNode = (TreeViewNode) getRoot();
for (TreeViewPath<? super E> path : selectedView.getPaths(elem))
{
rootNode.insertTreeViewPath(path);
}
}
}
// private void refreshElement(E elem)
// {
// if (dataElements.contains(elem) && selectedView != null)
// {
// TreeViewNode rootNode = (TreeViewNode) getRoot();
// for (TreeViewPath<? super E> path : selectedView.getPaths(elem))
// {
// rootNode.removeTreeViewPath(path);
// rootNode.insertTreeViewPath(path);
// }
// }
// }
public final TreeView<? super E> getSelectedTreeView()
{
return selectedView;
}
public final void setSelectedTreeView(TreeView<? super E> view)
{
if (view != null)
{
this.selectedView = view;
Vector<TreeViewPath<? super E>> paths = new Vector<>();
for (E element : dataElements)
{
for (TreeViewPath<? super E> path : view.getPaths(element))
{
paths.add(path);
}
}
setRoot(new TreeViewNode(paths));
}
}
@Override
public final int getColumnCount()
{
return datacolumns.size() + 1;
}
@Override
public Class<?> getColumnClass(int column)
{
return getDataColumn(column).getDataClass();
}
@Override
public String getColumnName(int column)
{
return getDataColumn(column).getName();
}
@Override
public boolean isCellEditable(Object node, int column)
{
if (getDataColumn(column).isEditable())
{
return column == 0 || dataElements.contains(((TreeViewNode) node).getUserObject());
}
return false;
}
private DataViewColumn getDataColumn(int column)
{
switch (column)
{
case 0:
return namecolumn;
default:
return datacolumns.get(column - 1);
}
}
@Override
public final void sortModel(Comparator<Row> comparator)
{
TreeViewNode rootNode = (TreeViewNode) getRoot();
rootNode.sortChildren(comparator);
reload();
}
private final class TreeViewNode extends JTree.DynamicUtilTreeNode
implements SortableTreeTableNode
{
private final int level;
public TreeViewNode(Vector<TreeViewPath<? super E>> paths)
{
this(0, null, paths);
}
private TreeViewNode(int level, Object name,
Vector<TreeViewPath<? super E>> paths)
{
super(name, paths);
this.level = level;
setAllowsChildren(true);
}
@Override
public int getLevel()
{
return level;
}
@Override
@SuppressWarnings("unchecked")
protected void loadChildren()
{
loadedChildren = true;
if (childValue != null)
{
ListMap<Object, TreeViewPath<? super E>, Vector<TreeViewPath<? super E>>> vectorMap = CollectionMaps.createListMap(
HashMap.class,
Vector.class);
Vector<TreeViewPath<? super E>> vector = (Vector<TreeViewPath<? super E>>) childValue;
for (TreeViewPath<? super E> path : vector)
{
if (path.getPathCount() > level)
{
Object key = path.getPathComponent(level);
vectorMap.add(key, path);
}
}
for (Object key : vectorMap.keySet())
{
vector = vectorMap.get(key);
TreeViewNode child;
// if (vector.size() == 1 && vector.firstElement().getPathCount() <= level + 1)
if (vector.size() == 1 && vector.firstElement().getPathCount() == level + 1)
{
child = new TreeViewNode(level + 1, key, null);
}
else
{
child = new TreeViewNode(level + 1, key, vector);
}
child.setComparator(mostRecentComparator);
if (mostRecentComparator == null || children == null)
{
add(child);
}
else
{
int index = Collections.binarySearch(children, child, mostRecentComparator);
if (index < 0)
{
insert(child, -(index + 1));
}
else
{
insert(child, index);
}
}
}
childValue = null;
}
}
public void removeTreeViewPath(TreeViewPath<? super E> path)
{
if (!loadedChildren)
{
Vector<TreeViewPath<? super E>> vector = (Vector<TreeViewPath<? super E>>) childValue;
vector.remove(path);
return;
}
Object levelObject = path.getPathComponent(level);
if (levelObject == null)
{
return;
}
for (int i = 0; i < getChildCount(); i++)
{
TreeViewNode child = (TreeViewNode) getChildAt(i);
if (child != null && levelObject.equals(child.userObject))
{
if (path.getPathCount() == level + 1)
{//its a leaf, so remove appropriate child
removeNodeFromParent(child);
}
else
{//its in a branch, so pass on the request to the child
child.removeTreeViewPath(path);
//make sure to remove the branch if it is no longer useful
if (!dataElements.contains(child.userObject) && child.getChildCount() == 0)
{
removeNodeFromParent(child);
}
}
return;
}
}
}
public void insertTreeViewPath(TreeViewPath<? super E> path)
{
// Logging.errorPrint("adding: "+path);
if (!loadedChildren)
{
Vector<TreeViewPath<? super E>> vector = (Vector<TreeViewPath<? super E>>) childValue;
vector.add(path);
return;
}
if (level >= path.getPathCount())
{
Logging.errorPrint("Ignoring attempt to add child at level "
+ level + " which is beyond end of path " + path);
return;
}
Object levelObject = path.getPathComponent(level);
if (mostRecentComparator == null)
{
for (int i = 0; i < getChildCount(); i++)
{
TreeViewNode child = (TreeViewNode) getChildAt(i);
if (levelObject.equals(child.userObject))
{
child.insertTreeViewPath(path);
return;
}
}
}
TreeViewNode newchild;
if (path.getPathCount() == level + 1)
{
newchild = new TreeViewNode(level + 1, levelObject, null);
}
else
{
Vector<TreeViewPath<? super E>> vector = new Vector<>();
vector.add(path);
newchild = new TreeViewNode(level + 1, levelObject, vector);
}
newchild.setComparator(mostRecentComparator);
if (mostRecentComparator == null || children == null)
{
insertNodeInto(newchild, this, getChildCount());
return;
}
int index = Collections.binarySearch(children, newchild, mostRecentComparator);
if (index >= 0)
{
TreeViewNode child = (TreeViewNode) getChildAt(index);
if (child.getLevel() >= path.getPathCount())
{
// Duplicate named entry - just add it to the tree.
insertNodeInto(newchild, this, (index + 1));
}
else
{
child.insertTreeViewPath(path);
}
}
else
{
insertNodeInto(newchild, this, -(index + 1));
}
}
@Override
public boolean isLeaf()
{
if (level == 0)
{
return false;
}
if (!loadedChildren)
{
return childValue == null;
}
else
{
return getChildCount() == 0;
}
}
private Comparator<Row> mostRecentComparator = null;
public void setComparator(Comparator<Row> comparator)
{
this.mostRecentComparator = comparator;
}
@Override
@SuppressWarnings("unchecked")
public void sortChildren(Comparator<Row> comparator)
{
setComparator(comparator);
if (!loadedChildren)
{
loadChildren();
}
if (children != null)
{
children.sort(comparator);
for (Object obj : children)
{
TreeViewNode child = (TreeViewNode) obj;
child.setComparator(mostRecentComparator);
if (child.loadedChildren)
{
child.sortChildren(comparator);
}
}
}
}
@Override
public Object getValueAt(int column)
{
if (column == 0)
{
return userObject;
}
if (dataElements.contains(userObject))
{
return dataview.getData((E) userObject, column - 1);
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public void setValueAt(Object value, int column)
{
if (dataElements.contains(userObject))
{
dataview.setData(value, (E) userObject, column - 1);
}
}
@Override
public TreeTableNode getChildAt(int childIndex)
{
return (TreeTableNode) super.getChildAt(childIndex);
}
@Override
public TreeTableNode getParent()
{
return (TreeTableNode) super.getParent();
}
}
}