001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.relation;
003
004import java.util.List;
005import java.util.concurrent.CopyOnWriteArrayList;
006
007import javax.swing.event.TreeModelEvent;
008import javax.swing.event.TreeModelListener;
009import javax.swing.tree.TreeModel;
010import javax.swing.tree.TreePath;
011
012import org.openstreetmap.josm.data.osm.Relation;
013import 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 javax.swing.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 */
024public 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<>();
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<>();
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<>();
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    @Override
192    public Object getChild(Object parent, int index) {
193        return getRelationChildByIdx((Relation)parent, index);
194    }
195
196    @Override
197    public int getChildCount(Object parent) {
198        return getNumRelationChildren((Relation)parent);
199    }
200
201    @Override
202    public int getIndexOfChild(Object parent, Object child) {
203        return getIndexForRelationChild((Relation)parent, (Relation)child);
204    }
205
206    @Override
207    public Object getRoot() {
208        return root;
209    }
210
211    @Override
212    public boolean isLeaf(Object node) {
213        Relation r = (Relation)node;
214        if (r.isIncomplete()) return false;
215        return getNumRelationChildren(r) == 0;
216    }
217
218    @Override
219    public void addTreeModelListener(TreeModelListener l) {
220        if (l != null) {
221            listeners.addIfAbsent(l);
222        }
223    }
224
225    @Override
226    public void removeTreeModelListener(TreeModelListener l) {
227        listeners.remove(l);
228    }
229
230    @Override
231    public void valueForPathChanged(TreePath path, Object newValue) {
232        // do nothing
233    }
234}