001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.dialogs.relation; 003 004 import java.util.List; 005 import java.util.concurrent.CopyOnWriteArrayList; 006 007 import javax.swing.event.TreeModelEvent; 008 import javax.swing.event.TreeModelListener; 009 import javax.swing.tree.TreeModel; 010 import javax.swing.tree.TreePath; 011 012 import org.openstreetmap.josm.data.osm.Relation; 013 import org.openstreetmap.josm.data.osm.RelationMember; 014 015 /** 016 * This is a {@link TreeModel} which provides the hierarchical structure of {@link Relation}s 017 * to a {@link JTree}. 018 * 019 * The model is initialized with a root relation or with a list of {@link RelationMember}s, see 020 * {@link #populate(Relation)} and {@link #populate(List)} respectively. 021 * 022 * 023 */ 024 public class RelationTreeModel implements TreeModel { 025 /** the root relation */ 026 private Relation root; 027 028 /** the tree model listeners */ 029 private CopyOnWriteArrayList<TreeModelListener> listeners; 030 031 /** 032 * constructor 033 */ 034 public RelationTreeModel() { 035 this.root = null; 036 listeners = new CopyOnWriteArrayList<TreeModelListener>(); 037 } 038 039 /** 040 * constructor 041 * @param root the root relation 042 */ 043 public RelationTreeModel(Relation root) { 044 this.root = root; 045 listeners = new CopyOnWriteArrayList<TreeModelListener>(); 046 } 047 048 /** 049 * constructor 050 * 051 * @param members a list of members 052 */ 053 public RelationTreeModel(List<RelationMember> members) { 054 if (members == null) return; 055 Relation root = new Relation(); 056 root.setMembers(members); 057 this.root = root; 058 listeners = new CopyOnWriteArrayList<TreeModelListener>(); 059 } 060 061 /** 062 * Replies the number of children of type relation for a particular 063 * relation <code>parent</code> 064 * 065 * @param parent the parent relation 066 * @return the number of children of type relation 067 */ 068 protected int getNumRelationChildren(Relation parent) { 069 if (parent == null) return 0; 070 int count = 0; 071 for(RelationMember member : parent.getMembers()) { 072 if (member.isRelation()) { 073 count++; 074 } 075 } 076 return count; 077 } 078 079 /** 080 * Replies the i-th child of type relation for a particular relation 081 * <code>parent</code>. 082 * 083 * @param parent the parent relation 084 * @param idx the index 085 * @return the i-th child of type relation for a particular relation 086 * <code>parent</code>; null, if no such child exists 087 */ 088 protected Relation getRelationChildByIdx(Relation parent, int idx) { 089 if (parent == null) return null; 090 int count=0; 091 for (RelationMember member : parent.getMembers()) { 092 if (!(member.isRelation())) { 093 continue; 094 } 095 if (count == idx) 096 return member.getRelation(); 097 count++; 098 } 099 return null; 100 } 101 102 /** 103 * Replies the index of a particular <code>child</code> with respect to its 104 * <code>parent</code>. 105 * 106 * @param parent the parent relation 107 * @param child the child relation 108 * @return the index of a particular <code>child</code> with respect to its 109 * <code>parent</code>; -1 if either parent or child are null or if <code>child</code> 110 * isn't a child of <code>parent</code>. 111 * 112 */ 113 protected int getIndexForRelationChild(Relation parent, Relation child) { 114 if (parent == null || child == null) return -1; 115 int idx = 0; 116 for (RelationMember member : parent.getMembers()) { 117 if (!(member.isRelation())) { 118 continue; 119 } 120 if (member.getMember() == child) return idx; 121 idx++; 122 } 123 return -1; 124 } 125 126 /** 127 * Populates the model with a root relation 128 * 129 * @param root the root relation 130 * @see #populate(List) 131 * 132 */ 133 public void populate(Relation root) { 134 if (root == null) { 135 root = new Relation(); 136 } 137 this.root = root; 138 fireRootReplacedEvent(); 139 } 140 141 /** 142 * Populates the model with a list of relation members 143 * 144 * @param members the relation members 145 */ 146 public void populate(List<RelationMember> members) { 147 if (members == null) return; 148 Relation r = new Relation(); 149 r.setMembers(members); 150 this.root = r; 151 fireRootReplacedEvent(); 152 } 153 154 /** 155 * Notifies tree model listeners about a replacement of the 156 * root. 157 */ 158 protected void fireRootReplacedEvent() { 159 TreeModelEvent e = new TreeModelEvent(this, new TreePath(root)); 160 for (TreeModelListener l : listeners) { 161 l.treeStructureChanged(e); 162 } 163 } 164 165 /** 166 * Notifies tree model listeners about an update of the 167 * trees nodes. 168 * 169 * @param path the tree path to the node 170 */ 171 protected void fireRefreshNode(TreePath path) { 172 TreeModelEvent e = new TreeModelEvent(this, path); 173 for (TreeModelListener l : listeners) { 174 l.treeStructureChanged(e); 175 } 176 177 } 178 179 /** 180 * Invoke to notify all listeners about an update of a particular node 181 * 182 * @param pathToNode the tree path to the node 183 */ 184 public void refreshNode(TreePath pathToNode) { 185 fireRefreshNode(pathToNode); 186 } 187 188 /* ----------------------------------------------------------------------- */ 189 /* interface TreeModel */ 190 /* ----------------------------------------------------------------------- */ 191 public Object getChild(Object parent, int index) { 192 return getRelationChildByIdx((Relation)parent, index); 193 } 194 195 public int getChildCount(Object parent) { 196 return getNumRelationChildren((Relation)parent); 197 } 198 199 public int getIndexOfChild(Object parent, Object child) { 200 return getIndexForRelationChild((Relation)parent, (Relation)child); 201 } 202 203 public Object getRoot() { 204 return root; 205 } 206 207 public boolean isLeaf(Object node) { 208 Relation r = (Relation)node; 209 if (r.isIncomplete()) return false; 210 return getNumRelationChildren(r) == 0; 211 } 212 213 public void addTreeModelListener(TreeModelListener l) { 214 if (l != null) { 215 listeners.addIfAbsent(l); 216 } 217 } 218 219 public void removeTreeModelListener(TreeModelListener l) { 220 listeners.remove(l); 221 } 222 223 public void valueForPathChanged(TreePath path, Object newValue) { 224 // do nothing 225 } 226 }