I am implementing TreeModel interface and have implemented all methods except for the valueForPathChanged.
In my case, the values are not going to be programatically changed.
Why TreeModel interface contains this method? Is it used by JTree in any circumstances, or I am safe to leave it unimplemented?
Code for the reference, it works. I am just concerned whether valueForPathChanged is required by the JTree:
class ParamsTreeModel implements TreeModel {
private final TreeRoot root;
private final List<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
ParamsTreeModel(TreeRoot root) {
this.root = root;
}
#Override
public void addTreeModelListener(TreeModelListener l) {
listeners.add(l);
}
#Override
public Object getChild(Object parent, int index) {
if(parent instanceof Param) return null;
if(structuredMap.containsKey(parent)) {
return structuredMap.get(parent).get(index);
}
// Root
return partNames.get(index);
}
#Override
public int getChildCount(Object parent) {
if(parent instanceof Param) return 0;
if(parent instanceof TreeRoot) return partNames.size();
return structuredMap.get(parent).size();
}
#Override
public int getIndexOfChild(Object parent, Object child) {
if(parent instanceof TreeRoot) return partNames.indexOf(child);
return structuredMap.get(parent).indexOf(child);
}
#Override
public Object getRoot() {
return root;
}
#Override
public boolean isLeaf(Object node) {
return (node instanceof Param);
}
#Override
public void removeTreeModelListener(TreeModelListener l) {
listeners.remove(l);
}
#Override
public void valueForPathChanged(TreePath path, Object newValue) {
// TODO Auto-generated method stub
}
}
While you are required to implement the valueForPathChanged() method, as defined in the TreeModel interface, you are free to leave it empty. FileTreeModel, cited here, is an example. The valueForPathChanged() method is typically used to support cell editing. As a concrete example, the implementation in DefaultTreeModel, seen here, "sets the user object of the TreeNode identified by path and posts a node changed [event]."
Related
In class Tree I got error message:
The method removeparent() is undefined for the type String.
I want to convert string "Grandchild3" to object which instance MyTreeNode class, then I can use removep("Grandchild3") call the method like this Grandchild3.removeparent().
How can I do this?
Here's class MyTreeNode:
public class MyTreeNode<T>{
private T data = null;
private List<MyTreeNode> children = new ArrayList<>();
private MyTreeNode parent = null;
public MyTreeNode(T data) {
this.data = data;
}
public void addChild(MyTreeNode child) {
child.setParent(this);
this.children.add(child);
}
public void addChild(T data) {
MyTreeNode<T> newChild = new MyTreeNode<>(data);
newChild.setParent(this);
children.add(newChild);
}
public void addChildren(List<MyTreeNode> children) {
for(MyTreeNode t : children) {
t.setParent(this);
}
this.children.addAll(children);
}
public List<MyTreeNode> getChildren() {
return children;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
private void setParent(MyTreeNode parent) {
this.parent = parent;
}
public MyTreeNode getParent() {
return parent;
}
public void removeparent() {
this.parent = null;
}
public void removeChild(MyTreeNode<T> child)
{
this.children.remove(child);
}
}
Here's class Tree:
public class Tree {
public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated method stub
MyTreeNode<String> root = new MyTreeNode<>("Root");
MyTreeNode<String> child1 = new MyTreeNode<>("Child1");
child1.addChild("Grandchild1");
child1.addChild("Grandchild2");
MyTreeNode<String> child2 = new MyTreeNode<>("Child2");
child2.addChild("Grandchild3");
root.addChild(child1);
root.addChild(child2);
root.addChild("Child3");
root.addChildren(Arrays.asList(
new MyTreeNode<>("Child4"),
new MyTreeNode<>("Child5"),
new MyTreeNode<>("Child6")
));
for(MyTreeNode<String> node : root.getChildren()) {
System.out.println(node.getData());
}
printTree(root, " ");
removep("Grandchild3"); //error message"The method removeparent() is undefined for the type String"
printTree(root, " ");
}
private static void printTree(MyTreeNode<String> node, String appender) {
System.out.println(appender+node.getData());
for (MyTreeNode each : node.getChildren()){
printTree(each, appender + appender);
}
}
public static void removep(MyTreeNode<String> node)
{
node.getParent().removeChild(node);
node.removeparent();
}
}
So basically what you want is to convert a string to a class object. Now there are probably several ways it could be done, but in the context used here it seems like you could simply pass the appropriate object instead of messing with strings.
So in your case, the most appropriate way to do so would be to create a method which receives a string name and the top node of your tree and iterates over the tree to find the node with the given name. When found the method returns the node.
Then you can use that method to get the node from the name and then call removep.
I am using a tree table model in my app which extends AbstractTreeTableModel in order to create a JXTreeTable. Below is my model.
import org.jdesktop.swingx.treetable.AbstractTreeTableModel;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableModel;
import javax.swing.tree.TreeModel;
public class MyDataModel extends AbstractTreeTableModel{
static protected String[] columnNames = { "Field", "Value" };
static protected Class<?>[] columnTypes = { Object.class, Object.class};
public MyDataModel(MyDataNode rootNode) {
super(rootNode);
root = rootNode;
}
#Override
public Object getChild(Object parent, int index) {
return ((MyDataNode) parent).getChildren().get(index);
}
#Override
public int getChildCount(Object parent) {
return ((MyDataNode) parent).getChildren().size();
}
#Override
public int getIndexOfChild(Object parent, Object child) {
return 0;
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Class<?> getColumnClass(int column) {
return columnTypes[column];
}
#Override
public Object getValueAt(Object node, int column) {
MyDataNode mNode=(MyDataNode)node;
Object obj =mNode.getNodeDataObject();
if(column==0){
return mNode.getName();
}
else if (column==1){
if(obj instanceof Field){
Field field=(Field)mNode.getNodeDataObject();
if(field.getFieldDef().getListValue().size()>0){
return field.getFieldDef().getListValue();
}
else
return mNode.getDefaultValue();
}
else
return mNode.getDefaultValue();
}
return null;
}
#Override
public boolean isCellEditable(Object node, int column) {
//only allow Field values to be editable
if(((MyDataNode)node).getNodeDataObject() instanceof Field && column==1)
return true;
else
return false;
}
#Override
public boolean isLeaf(Object node) {
MyDataNode mNode=(MyDataNode)node;
Object obj=mNode.getNodeDataObject();
if(obj instanceof Field){
Field field=(Field)obj;
if(field.getFieldDef().getDataType().equalsIgnoreCase("MULTIPLE_MESSAGE")){
return false;
}
else
return true;
}
return false;
}
#Override
public void setValueAt(Object aValue, Object node, int column) {
MyDataNode mNode=(MyDataNode)node;
if (mNode.getNodeDataObject() instanceof Field && column == 1) {
Field field = (Field) mNode.getNodeDataObject();
field.setDefaultValue(aValue);
field.setSelectedValue(aValue);
}
}
}
This is how I use the JXTreeTable in my app
MyDataModel treeTableModel = new MyDataModel(createDataStructure(message));
jTreeTable = new JXTreeTable(treeTableModel);
private static MyDataNode createDataStructure(Message message) {
//setting fields as children of the root
nodeList = new ArrayList<>();
for (int index=0;index<message.getListFields().size() ; index++) {
if(message.getListFields().get(index).getFieldDef()
.getDataType().equalsIgnoreCase("MULTIPLE_MESSAGE")){
nodeList.add(new MyDataNode(message.getListFields()
.get(index).getFieldDef().getfName(), "", childMessagesRoot,
message.getListFields().get(index)));
}
else{
nodeList.add(new MyDataNode(message.getListFields()
.get(index).getFieldDef().getfName(), (String)(message.getListFields().
get(index).getDefaultValue()),
null,message.getListFields().get(index)));
}
}
//setting the Message to the root of the tree
root = new MyDataNode(message.getMsgName(), "", nodeList,message);
return root;
}
when I need to add a new node to the JXTreeTable, I try to get its model and use insertNodeInto() function call but the model doesn't support the insertNodeInto() function.
Someone please let me know where I am going wrong in the code. This is the first time I am using tree tables so there could be something missing.
insertNodeInto is a method of DefaultTreeTableModel, but not one of AbstractTreeTableModel.
Let MyDataModel extend DefaultTreeTableModel rather than AbstractTreeTableModel to be able to use insertNodeInto.
When accessing the TreeTableModel through JXTreeTable#getTreeTableModel, remember to cast the returned object of type TreeTableModel to DefaultTreeTableModel before calling insertNodeInto.
I found where I had gone wrong. I extended MyDataModel class with DefaultTreeTableModel and made my node class(I am using a custom node class) extend DefaultMutableTreeTableNode this gave the solution to use insertNodeInto method to get the current selected node of the tree table.
I try to use DataBinding on SWT Widgets.
I´m wondering if there is a way to connect a Combo Box to an underlying String in the model.
So I have a String in the Model and a Combo on my View?
As the standard way is not working:
//View
DataBindingContext ctx = new DataBindingContext();
IObservableValue target1 = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue model1 = BeanProperties.value(OutputVariable.class, "type").observe(outputVariable);
ctx.bindValue(target1, model1);
//Model
public void setType(String type) {
//TYPES is a constant with the possible Combo values
if (contains(TYPES, type)) {
String oldType = this.type;
this.type = type;
firePropertyChange("type", oldType, this.type);
}else {
throw new IllegalArgumentException();
}
}
I tried to use the fireIndexedPropertyChangeMethod which didn't worked either.
Is there a way to connect those two together? Maybe I have to use another WidgetProperties or BeanProperties method?
As a workaround I could maybe use a new Property in the model, which defines the combo selection index, connect this to the Combo and transfer changes of this index to the type Property and vice versa. But that seems not as a great solution to me.
Edit:
The Solution with a selectionIndex Property is working. But a cleaner method would still be nice as now a type Property change in the model has to reset the selectionIndex too and vice versa.
I have a clean solution now, which is to use a Converter.
//View
IObservableValue comboObservable = WidgetProperties.singleSelectionIndex().observe(combo);
IObservableValue viewTypeObservable = BeanProperties.value(DebugModel.class, "type").observe(debugModel);
IConverter viewTypeToIntConverter = createViewTypeToIntConverter();
UpdateValueStrategy toTargetStrategy = new UpdateValueStrategy();
toTargetStrategy.setConverter(viewTypeToIntConverter);
IConverter intToViewTypeConverter = createIntToViewTypeConverter();
UpdateValueStrategy toModelStrategy = new UpdateValueStrategy();
toModelStrategy.setConverter(intToViewTypeConverter);
DataBindingContext context = new DataBindingContext();
context.bindValue(comboObservable, viewTypeObservable, toModelStrategy, toTargetStrategy);
//Converter
private IConverter createIntToViewTypeConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof Integer) {
for(ViewType type : ViewType.values()) {
if(type.toString().equals(ViewType.getStringAtIndex((int)value))) {
return type;
}
}
}
throw new IllegalArgumentException("We need an Integer to convert it but got an " + value.getClass());
}
#Override
public Object getFromType() {
return Integer.class;
}
#Override
public Object getToType() {
return ViewType.class;
}
};
}
private IConverter createViewTypeToIntConverter() {
return new IConverter() {
#Override
public Object convert(Object value) {
if(value instanceof ViewType) {
String[] viewTypes = ViewType.getStringValues();
for(int i=0;i<viewTypes.length;i++) {
if(viewTypes[i].equals(((ViewType)value).toString())) {
return i;
}
}
}
throw new IllegalArgumentException("We need a View Type to be converted but got a " + value.getClass());
}
#Override
public Object getFromType() {
return ViewType.class;
}
#Override
public Object getToType() {
return Integer.class;
}
};
}
//Model
public class DebugModel extends ModelObject {
private ViewType type;
public ViewType getType() {
return type;
}
public void setType(ViewType type) {
firePropertyChange("type", this.type, this.type = type);
}
}
//Just to complete the example, be sure the Model class extends a ModelObject class like this
public class ModelObject {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
protected void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) {
changeSupport.fireIndexedPropertyChange(propertyName, index, oldValue, newValue);
}
}
Of course you can outsource the Converters to custom classes, I used it this way just to show a quick solution here.
I have a ContentProvider for a TreeSelectionDialog. I need to implement the getParent method in order to select the root of a tree if one of its nodes is checked. This is the code:
#SuppressWarnings("unchecked")
protected Node<T> getAdapter(Object element) {
if(element instanceof Tree)
return ((Tree<T>)element).getRootElement();
else
return (Node<T>)element;
}
#Override
public void dispose() {
// TODO Auto-generated method stub
}
#Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// TODO Auto-generated method stub
}
#Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
#Override
public Object[] getChildren(Object parentElement) {
if(parentElement instanceof org.db.normalization.Table) {
if(((org.db.normalization.Table)parentElement).getStatus() == Status.DELETED)
return new Object[0];
List<org.db.normalization.Attribute> atts = new ArrayList<org.db.normalization.Attribute>();
for(Attribute a:((org.db.normalization.Table)parentElement).getAttributes().getAttributes())
if(a.getStatus() != Status.UNMODIFIED)
atts.add(a);
for(Attribute a:((org.db.normalization.Table)parentElement).getPrimaryKey().getAttributes())
if(a.getStatus() != Status.UNMODIFIED)
atts.add(a);
return atts.toArray();
} else if (parentElement instanceof org.db.normalization.Attribute) {
return new Object[0];
} else {
#SuppressWarnings("unchecked")
List<org.db.normalization.Table> n = (ArrayList<org.db.normalization.Table>)parentElement;
if (n.size() > 0) {
return n.toArray() ;
}
}
return new Object[0];
}
#Override
public Object getParent(Object element) {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean hasChildren(Object element) {
// TODO Auto-generated method stub
return getChildren(element).length > 0;
}
I really have no idea of what to write in the getParent method, since I have no other information than the element received as a parameter, and this element alone, doesn't know its parent.
Thanks!
Most instances of a tree implementation, you do know your parent, so parents are either set by a setter method or on the constructor. You have no idea who the parent is, so you are presenting the worse case, where you basically have to get all node, and check rather the children of each node contain you.
I have a JTree implementation for directories in the file system. The problem I have is that when a directory is removed or renamed, the tree still shows the directory in it's parent. The parent tree seems not to be updated for any changes once a node has been expanded.
The model I have written does not cache (I have commented out what limited caching it did have) - it's like the JTree itself has cached the node.
The code (the model is a nested subclass):
public class FileSystemTree
extends JTree
{
// *****************************************************************************
// INSTANCE CREATE/DELETE
// *****************************************************************************
public FileSystemTree() {
this(new Model(null,null,null));
}
public FileSystemTree(String startPath) {
this(new Model(startPath,null,null));
}
public FileSystemTree(FileSelector inc, FileSelector exc) {
this(new Model(null,inc,exc));
}
public FileSystemTree(String startPath, FileSelector inc, FileSelector exc) {
this(new Model(startPath,inc,exc));
}
private FileSystemTree(Model model) {
super(model);
//setLargeModel(true);
setRootVisible(false);
setShowsRootHandles(true);
putClientProperty("JTree.lineStyle","Angled");
}
// *****************************************************************************
// INSTANCE METHODS - ACCESSORS
// *****************************************************************************
public Object getRoot() {
return getModel().getRoot();
}
// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************
public String convertValueToText(Object value,boolean selected,boolean expanded,boolean leaf,int row,boolean hasFocus) {
File fil=(File)value;
return (fil.getName().length()!=0 ? fil.getName() : fil.getPath());
}
// *****************************************************************************
// STATIC NESTED CLASSES - SUPPORTING MODEL
// *****************************************************************************
static class Model
extends Object
implements TreeModel, FilenameFilter
{
private File root; // tree root
//ivate Map cache; // caches child counts for directories
private File[] fsRoots; // copy of file system roots
private FileSelector include; // inclusion selector
private FileSelector exclude; // exclusion selector
private java.util.List listeners=new ArrayList();
public Model(String roo, FileSelector inc, FileSelector exc) {
super();
root=(roo==null ? DRIVES : new File(roo));
//cache=new HashMap();
fsRoots=(root==DRIVES ? rootList() : null);
include=inc;
exclude=exc;
}
// *****************************************************************************
// METHODS - MODEL
// *****************************************************************************
public Object getRoot() {
return root;
}
public Object getChild(Object parent, int index) {
File dir=(File)parent;
if(dir==DRIVES) {
File[] chl=fsRoots; //rootList();
return (index<chl.length ? chl[index] : null);
}
else {
String[] chl=dirList(dir);
return (index<chl.length ? new File(dir,chl[index]) : null);
}
}
public int getChildCount(Object parent) {
File dir=(File)parent;
//Integer cch=(Integer)cache.get(dir);
//if(cch!=null) {
// return cch.intValue();
// }
if(dir==DRIVES) {
return fsRoots.length; //rootList().length;
}
else if(dir.isDirectory()) {
return dirList(dir).length;
}
else {
return 0;
}
}
public boolean isLeaf(Object node) {
return((File)node).isFile();
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public int getIndexOfChild(Object parent, Object child) {
File dir=(File)parent;
File fse=(File)child;
if(dir==DRIVES) {
File[] ca=fsRoots; //rootList();
for(int xa=0; xa<ca.length; xa++) {
if(fse.equals(ca[xa])) { return xa; }
}
}
else {
String[] ca=dirList(dir);
for(int xa=0; xa<ca.length; ++xa) {
if(fse.getName().equals(ca[xa])) { return xa; }
}
}
return -1;
}
private File[] rootList() {
File[] lst=File.listRoots();
if(lst==null) { lst=new File[0]; }
//cache.put(DRIVES,new Integer(lst.length));
return lst;
}
private String[] dirList(File dir) {
String[] lst=dir.list(this);
if(lst==null) { lst=new String[0]; }
//cache.put(dir,new Integer(lst.length));
return lst;
}
// *****************************************************************************
// METHODS - FILENAME FILTER
// *****************************************************************************
public boolean accept(File dir, String nam) {
return ((include==null || include.accept(dir,nam)) && (exclude==null || !exclude.accept(dir,nam)));
}
// *****************************************************************************
// METHODS - LISTENER
// *****************************************************************************
public void addTreeModelListener(TreeModelListener listener) {
if(listener != null && !listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeTreeModelListener(TreeModelListener listener) {
if(listener != null) {
listeners.remove(listener);
}
}
public void fireTreeNodesChanged(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesChanged(evt);
}
}
public void fireTreeNodesInserted(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesInserted(evt);
}
}
public void fireTreeNodesRemoved(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeNodesRemoved(evt);
}
}
public void fireTreeStructureChanged(TreeModelEvent evt) {
Iterator itr=listeners.iterator();
while(itr.hasNext()) {
TreeModelListener listener=(TreeModelListener)itr.next();
listener.treeStructureChanged(evt);
}
}
} // END INNER CLASS
// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************
static private final File DRIVES=new File("*DRIVES"); // marker for listing file system drives
} // END PUBLIC CLASS
The model is responsible for notifying the listeners of any modification in the tree structure. The tree will not refresh itself in absence of such a notification.
You could write some background thread that looks for changes in the filesystem and fire some model modification events if you detect that the directories have changed.
Without seeing the code for the Model, are you sure it is being called, such as the getChild method?