001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.component.model;
020
021import icy.util.StringUtil;
022
023import java.util.ArrayList;
024import java.util.List;
025
026import javax.swing.event.EventListenerList;
027import javax.swing.event.TreeModelEvent;
028import javax.swing.event.TreeModelListener;
029import javax.swing.tree.TreeModel;
030import javax.swing.tree.TreePath;
031
032import org.w3c.dom.Document;
033import org.w3c.dom.Element;
034import org.w3c.dom.NamedNodeMap;
035import org.w3c.dom.Node;
036import org.w3c.dom.NodeList;
037
038/**
039 * @author Stephane
040 */
041public class XMLTreeModel implements TreeModel
042{
043    public static class XMLAdapterNode
044    {
045        public Node node;
046
047        /**
048         * Creates a new instance of the XMLAdapterNode class
049         */
050        public XMLAdapterNode(Node node)
051        {
052            super();
053
054            this.node = node;
055        }
056
057        /**
058         * Return all children
059         */
060        public List<Node> getChildren()
061        {
062            final List<Node> result = new ArrayList<Node>();
063
064            if (node.hasAttributes())
065            {
066                final NamedNodeMap attributes = node.getAttributes();
067                final int count = attributes.getLength();
068
069                for (int i = 0; i < count; i++)
070                    result.add(attributes.item(i));
071            }
072
073            final NodeList nodes = node.getChildNodes();
074            final int count = nodes.getLength();
075
076            for (int i = 0; i < count; i++)
077            {
078                final Node node = nodes.item(i);
079
080                if (node instanceof Element)
081                    result.add(node);
082            }
083
084            return result;
085        }
086
087        /**
088         * Return index of child in this node.
089         * 
090         * @param child
091         *        The child to look for
092         * @return index of child, -1 if not present (error)
093         */
094        public int index(XMLAdapterNode child)
095        {
096            int result = 0;
097
098            for (Node node : getChildren())
099            {
100                if (child.node == node)
101                    return result;
102
103                result++;
104            }
105
106            return -1; // Should never get here.
107        }
108
109        /**
110         * Returns an adapter node given a valid index found through
111         * the method: public int index(XMLAdapterNode child)
112         * 
113         * @param index
114         *        find this by calling index(XMLAdapterNode)
115         * @return the desired child
116         */
117        public XMLAdapterNode child(int index)
118        {
119            final Node n = getChildren().get(index);
120
121            if (n == null)
122                return null;
123
124            return new XMLAdapterNode(n);
125        }
126
127        /**
128         * Return the number of element children for this element/node
129         * 
130         * @return int number of element children
131         */
132        public int childCount()
133        {
134            return getChildren().size();
135        }
136
137        /**
138         * Return the value of this node from its sub text nodes
139         */
140        protected String getValue()
141        {
142            final NodeList nodes = node.getChildNodes();
143            final int count = nodes.getLength();
144            String result = "";
145
146            for (int i = 0; i < count; i++)
147            {
148                final Node node = nodes.item(i);
149
150                // text node
151                if (!(node instanceof Element))
152                {
153                    final String value = node.getNodeValue();
154
155                    if ((value != null) && !StringUtil.equals(value, "null"))
156                        result += value + " ";
157                }
158            }
159
160            return result.trim();
161        }
162
163        @Override
164        public String toString()
165        {
166            final String nodeName = node.getNodeName();
167            final String nodeValue = node.getNodeValue();
168
169            if (!StringUtil.isEmpty(nodeValue) && !StringUtil.equals(nodeValue, "null"))
170                return nodeName + " = " + nodeValue;
171
172            return nodeName;
173        }
174    }
175
176    protected Document document;
177
178    /**
179     * listeners
180     */
181    protected EventListenerList listeners = new EventListenerList();
182
183    public XMLTreeModel(Document doc)
184    {
185        super();
186
187        if (doc == null)
188            throw new NullPointerException();
189
190        document = doc;
191    }
192
193    @Override
194    public Object getRoot()
195    {
196        if (document.getDocumentElement() == null)
197            return null;
198
199        return new XMLAdapterNode(document.getDocumentElement());
200    }
201
202    @Override
203    public Object getChild(Object parent, int index)
204    {
205        return ((XMLAdapterNode) parent).child(index);
206    }
207
208    @Override
209    public int getIndexOfChild(Object parent, Object child)
210    {
211        return ((XMLAdapterNode) parent).index((XMLAdapterNode) child);
212    }
213
214    @Override
215    public int getChildCount(Object parent)
216    {
217        return ((XMLAdapterNode) parent).childCount();
218    }
219
220    @Override
221    public boolean isLeaf(Object node)
222    {
223        return ((XMLAdapterNode) node).childCount() == 0;
224    }
225
226    @Override
227    public void valueForPathChanged(TreePath path, Object newValue)
228    {
229        // ignore here
230    }
231
232    /*
233     * Use these methods to add and remove event listeners.
234     * (Needed to satisfy TreeModel interface, but not used.)
235     */
236
237    /**
238     * Adds a listener for the TreeModelEvent posted after the tree changes.
239     * 
240     * @see #removeTreeModelListener
241     * @param l
242     *        the listener to add
243     */
244    @Override
245    public void addTreeModelListener(TreeModelListener l)
246    {
247        listeners.add(TreeModelListener.class, l);
248    }
249
250    /**
251     * Removes a listener previously added with <B>addTreeModelListener()</B>.
252     * 
253     * @see #addTreeModelListener
254     * @param l
255     *        the listener to remove
256     */
257    @Override
258    public void removeTreeModelListener(TreeModelListener l)
259    {
260        listeners.remove(TreeModelListener.class, l);
261    }
262
263    public void fireTreeNodesChanged(TreeModelEvent e)
264    {
265        for (TreeModelListener listener : listeners.getListeners(TreeModelListener.class))
266            listener.treeNodesChanged(e);
267    }
268
269    public void fireTreeNodesInserted(TreeModelEvent e)
270    {
271        for (TreeModelListener listener : listeners.getListeners(TreeModelListener.class))
272            listener.treeNodesInserted(e);
273    }
274
275    public void fireTreeNodesRemoved(TreeModelEvent e)
276    {
277        for (TreeModelListener listener : listeners.getListeners(TreeModelListener.class))
278            listener.treeNodesRemoved(e);
279    }
280
281    public void fireTreeStructureChanged(TreeModelEvent e)
282    {
283        for (TreeModelListener listener : listeners.getListeners(TreeModelListener.class))
284            listener.treeStructureChanged(e);
285    }
286}