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 }