View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.table;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.Iterator;
26  
27  public class DefaultColumnTreeTableModel extends AbstractColumnTreeTableModel
28      implements SortableModel {
29      
30      //--- Constant(s) ---
31      
32      //--- Data field(s) ---
33  
34      private boolean ascending;
35      private boolean maintainSortOrder;
36      private int sortedColumn = -1;
37  
38      /***
39       * Stores the data.
40       */
41      private ArrayList rows = new ArrayList();
42  
43      //--- Constructor(s) ---
44  
45      public DefaultColumnTreeTableModel()
46      {
47      }
48  
49      //--- Method(s) ---
50  
51      /***
52       * Removes all nodes.
53       */
54      void clear()
55      {
56  		rows.clear();
57  		fireTreeStructureChanged(this, new Object[] { this }, null, null);
58      }
59  
60      /***
61       * Invoked by a {@link TreeTableNode} object when a child item was
62       * added.
63       */
64      void childInserted(TreeTableNode node, int index, Object child)
65      {
66  		fireTreeNodesInserted(this, new Object[] { this, node }, 
67  							  new int[] { index }, 
68  							  new Object[] { child });
69      }
70  
71      /***
72       * Invoked by a {@link TreeTableNode} object when a child item has
73       * changed its state.
74       */
75      void childChanged(TreeTableNode node, int index, Object child)
76      {
77  		fireTreeNodesChanged(this, new Object[] { this, node }, 
78  							 new int[] { index }, 
79  							 new Object[] { child });
80      }
81  
82      /***
83       * Invoked by a {@link TreeTableNode} object when a child item was
84       * removed.
85       */
86      void childRemoved(TreeTableNode node, int index, Object child)
87      {
88  		fireTreeNodesRemoved(this, new Object[] { this, node }, 
89  							 new int[] { index }, 
90  							 new Object[] { child });
91      }
92  
93      /***
94       * Invoked by a {@link TreeTableNode} object when it has
95       * changed its state.
96       */
97      void nodeChanged(TreeTableNode node)
98      {
99  		int index = rows.indexOf(node);
100 		if (index != -1) {
101 			fireTreeNodesChanged(this, new Object[] { this }, 
102 								 new int[] { index  }, 
103 								 new Object[] { node });
104 		}
105     }
106 
107     public void add(TreeTableNode node)
108     {
109 		rows.add(node);
110 		fireTreeNodesInserted(this, new Object[] { this }, 
111 							  new int[] { rows.size() - 1 }, 
112 							  new Object[] { node });
113 		
114 		if (maintainSortOrder && sortedColumn != -1) {
115 			sortByColumn(sortedColumn, ascending, false);
116 		}
117     }
118 
119     public Object getChild(Object node, int index)
120     {
121 		if (node == this) {
122 			return rows.get(index);
123 		}
124 		else {
125 			return ((TreeTableNode)node).getChildAt(index);
126 		}
127     }
128 
129     public int getChildCount(Object node)
130     {
131 		if (node == this) {
132 			return rows.size();
133 		}
134 		else if (node instanceof TreeTableNode) {
135 			return ((TreeTableNode)node).getChildCount();
136 		}
137 		else {
138 			return 0;
139 		}
140     }
141 
142     /***
143      * Returns the node that stores <code>data</code>.
144      *
145      * @return -1, if data could not be found
146      */
147     public int indexOfByData(Object data)
148     {
149 		for (int i = 0; i < rows.size(); i++) {
150 			TreeTableNode node = (TreeTableNode)rows.get(i);
151 			if (node.getData() == data) {
152 				return i;
153 			}
154 		}
155 		return -1;
156     }
157 
158     public Object getValueAt(Object node, int column)
159     {
160 		return null;
161     }
162 
163     public Iterator iterator()
164     {
165 		return rows.iterator();
166     }
167 
168     public void remove(TreeTableNode node)
169     {
170 		int index = rows.indexOf(node);
171 		if (index != -1) {
172 			remove(index);
173 		}
174     }
175 
176     public void remove(int index)
177     {
178 		Object node = rows.get(index);
179 		rows.remove(index);
180 		fireTreeNodesRemoved(this, new Object[] { this }, 
181 							 new int[] { index }, 
182 							 new Object[] { node });
183     }
184 
185     public int getSortedColumn()
186     {
187 		return sortedColumn;
188     }
189 
190     public boolean isSortedAscending()
191     {
192 		return ascending;
193     }
194 
195     /***
196      * Sets the maintain sort order flag.
197      */
198     public void setMaintainSortOrder(boolean newValue)
199     {
200 		maintainSortOrder = newValue;
201     }
202     
203     /***
204      * Sorts the table by <code>column</code>.
205      */
206     public boolean sortByColumn(int column, boolean ascending, boolean revert)
207     {
208 		if (column < 0 || column >= getColumnCount()) {
209 			throw new IllegalArgumentException();
210 		}
211 
212 		this.ascending = ascending;
213 		sortedColumn = column;
214 
215 		ArrayList copy = null;
216 		if (revert) {
217 			copy = (ArrayList)rows.clone();
218 		}
219 
220 		// the first column only contains the icon, but the value is from
221 		// the invisible second icon
222 		boolean hack
223 			= (sortedColumn == 0 && getColumnAt(0).getKey().equals("icon"));
224 		Comparator comparator = new TreeNodeComparator(hack ? 1 : sortedColumn);
225 		Collections.sort(rows, comparator);
226 
227 		if (revert && rows.equals(copy)) {
228 			this.ascending = !ascending;
229 			Collections.reverse(rows);
230 		}
231 
232 		for (Iterator i = rows.iterator(); i.hasNext();) {
233 			((TreeTableNode)i.next()).sort(comparator);
234 		}
235 
236 		fireTreeStructureChanged(this, new Object[] { getRoot() }, null, null); 
237 		return this.ascending;
238     }
239 
240     /***
241      * Compares {@link TreeTableNode} data objects.
242      */
243     private class TreeNodeComparator implements Comparator
244     {
245 
246 		private int column;
247 
248 		public TreeNodeComparator(int column)
249 		{
250 			this.column = column;
251 		}
252 
253 		public int compare(Object node1, Object node2) 
254 		{
255 			int order = compareByData(node1, node2);
256 			return ascending ? order : -order;
257 		}
258 
259 		public int compareByData(Object node1, Object node2) 
260 		{
261 			Class type = getColumnClass(column);
262 
263 			// check for nulls
264 			Object o1 = getValueAt(node1, column);
265 			Object o2 = getValueAt(node2, column);
266 
267 			// If both values are null return 0
268 			// define: null is less than anything
269 			if (o1 == null && o2 == null) {
270 				return 0;
271 			} else if (o1 == null) { 
272 				return -1;
273 			} else if (o2 == null) {
274 				return 1;
275 			}
276 
277 			if (type.equals(String.class)) {
278 				String s1  = (String)o1;
279 				String s2  = (String)o2;
280 				int result = s1.compareToIgnoreCase(s2);
281 		
282 				// empty strings come last
283 				if (s1.length() == 0 ^ s2.length() == 0) {
284 					result = -result;
285 				}
286 		
287 				return result;
288 			}
289 			else if (o1 instanceof Comparable) {
290 				return ((Comparable)o1).compareTo(o2);
291 			}
292 			else if (type == Boolean.class) {
293 				boolean b1 = ((Boolean)o1).booleanValue();
294 				boolean b2 = ((Boolean)o2).booleanValue();
295 		
296 				// define: false < true
297 				if (b1 == b2) {
298 					return 0;
299 				} else if (b1) {
300 					return 1;
301 				} else {
302 					return -1;
303 				}
304 			} 
305 			else {
306 				// now what?
307 				return o1.toString().compareTo(o2.toString());
308 			}
309 		}
310     }
311 
312 }