View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.magicproject.ui;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Vector;
28  
29  import javax.swing.AbstractListModel;
30  import javax.swing.JLabel;
31  
32  import net.sf.magicproject.tools.MCardCompare;
33  
34  /***
35   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
36   * @param <T>
37   *          the model type.
38   * @since 0.94
39   */
40  public class MListModel<T extends MCardCompare> extends AbstractListModel {
41  
42  	/***
43  	 * The content.
44  	 */
45  	public final Vector<T> delegate = new Vector<T>();
46  
47  	/***
48  	 * The original content.
49  	 */
50  	public final List<T> removedDelegate = new ArrayList<T>();
51  
52  	/***
53  	 * The label reflecting the amount of this list.
54  	 */
55  	private final JLabel linkedAmount;
56  
57  	/***
58  	 * If <code>true</code> the amount lable is updated with the internal
59  	 * amount.
60  	 */
61  	private final boolean deepSum;
62  
63  	/***
64  	 * Create a new instance of this class.
65  	 * 
66  	 * @param linkedAmount
67  	 *          the label reflecting the amount of this list.
68  	 * @param deepSum
69  	 *          if <code>true</code> the amount lable is updated with the
70  	 *          internal amount.
71  	 */
72  	public MListModel(JLabel linkedAmount, boolean deepSum) {
73  		super();
74  		this.linkedAmount = linkedAmount;
75  		this.deepSum = deepSum;
76  		updateAmount();
77  	}
78  
79  	/***
80  	 * Returns the number of components in this list.
81  	 * <p>
82  	 * This method is identical to <code>size</code>, which implements the
83  	 * <code>List</code> interface defined in the 1.2 Collections framework.
84  	 * This method exists in conjunction with <code>setSize</code> so that
85  	 * <code>size</code> is identifiable as a JavaBean property.
86  	 * 
87  	 * @return the number of components in this list
88  	 * @see #size()
89  	 */
90  	public int getSize() {
91  		return delegate.size();
92  	}
93  
94  	/***
95  	 * Returns the component at the specified index. <blockquote> <b>Note:</b>
96  	 * Although this method is not deprecated, the preferred method to use is
97  	 * <code>get(int)</code>, which implements the <code>List</code>
98  	 * interface defined in the 1.2 Collections framework. </blockquote>
99  	 * 
100 	 * @param index
101 	 *          an index into this list
102 	 * @return the component at the specified index
103 	 * @exception ArrayIndexOutOfBoundsException
104 	 *              if the <code>index</code> is negative or greater than the
105 	 *              current size of this list
106 	 * @see #get(int)
107 	 */
108 	public T getElementAt(int index) {
109 		return delegate.get(index);
110 	}
111 
112 	/***
113 	 * Returns the number of components in this list.
114 	 * 
115 	 * @return the number of components in this list
116 	 * @see List#size()
117 	 */
118 	public int size() {
119 		return delegate.size();
120 	}
121 
122 	/***
123 	 * Tests whether this list has any components.
124 	 * 
125 	 * @return <code>true</code> if and only if this list has no components,
126 	 *         that is, its size is zero; <code>false</code> otherwise
127 	 * @see List#isEmpty()
128 	 */
129 	public boolean isEmpty() {
130 		return delegate.isEmpty();
131 	}
132 
133 	/***
134 	 * Returns an enumeration of the components of this list.
135 	 * 
136 	 * @return an enumeration of the components of this list
137 	 * @see List#iterator()
138 	 */
139 	public Iterator<T> iterator() {
140 		return delegate.iterator();
141 	}
142 
143 	/***
144 	 * Tests whether the specified object is a component in this list.
145 	 * 
146 	 * @param elem
147 	 *          an object
148 	 * @return <code>true</code> if the specified object is the same as a
149 	 *         component in this list
150 	 * @see List#contains(Object)
151 	 */
152 	public boolean contains(T elem) {
153 		return delegate.contains(elem);
154 	}
155 
156 	/***
157 	 * Searches for the first occurrence of <code>elem</code>.
158 	 * 
159 	 * @param elem
160 	 *          an object
161 	 * @return the index of the first occurrence of the argument in this list;
162 	 *         returns <code>-1</code> if the object is not found
163 	 * @see List#indexOf(Object)
164 	 */
165 	public int indexOf(T elem) {
166 		return delegate.indexOf(elem);
167 	}
168 
169 	/***
170 	 * Searches for the first occurrence of <code>elem</code>.
171 	 * 
172 	 * @param elem
173 	 *          an object
174 	 * @return the index of the first occurrence of the argument in this list;
175 	 *         returns <code>-1</code> if the object is not found
176 	 * @see List#indexOf(Object)
177 	 */
178 	public int indexOf(String elem) {
179 		for (int i = 0; i < delegate.size(); i++) {
180 			if (delegate.get(i).equals(elem))
181 				return i;
182 		}
183 		return -1;
184 	}
185 
186 	/***
187 	 * Returns the index of the last occurrence of <code>elem</code>.
188 	 * 
189 	 * @param elem
190 	 *          the desired component
191 	 * @return the index of the last occurrence of <code>elem</code> in the
192 	 *         list; returns <code>-1</code> if the object is not found
193 	 * @see List#lastIndexOf(Object)
194 	 */
195 	public int lastIndexOf(T elem) {
196 		return delegate.lastIndexOf(elem);
197 	}
198 
199 	/***
200 	 * Returns the component at the specified index. Throws an
201 	 * <code>ArrayIndexOutOfBoundsException</code> if the index is negative or
202 	 * not less than the size of the list. <blockquote> <b>Note:</b> Although
203 	 * this method is not deprecated, the preferred method to use is
204 	 * <code>get(int)</code>, which implements the <code>List</code>
205 	 * interface defined in the 1.2 Collections framework. </blockquote>
206 	 * 
207 	 * @param index
208 	 *          an index into this list
209 	 * @return the component at the specified index
210 	 * @see #get(int)
211 	 * @see List#get(int)
212 	 */
213 	public T elementAt(int index) {
214 		return delegate.get(index);
215 	}
216 
217 	/***
218 	 * Sets the component at the specified <code>index</code> of this list to be
219 	 * the specified object. The previous component at that position is discarded.
220 	 * <p>
221 	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index is
222 	 * invalid. <blockquote> <b>Note:</b> Although this method is not deprecated,
223 	 * the preferred method to use is <code>set(int,Object)</code>, which
224 	 * implements the <code>List</code> interface defined in the 1.2 Collections
225 	 * framework. </blockquote>
226 	 * 
227 	 * @param obj
228 	 *          what the component is to be set to
229 	 * @param index
230 	 *          the specified index
231 	 * @see List#set(int, Object)
232 	 */
233 	public void setElementAt(T obj, int index) {
234 		delegate.set(index, obj);
235 		fireContentsChanged(this, index, index);
236 	}
237 
238 	/***
239 	 * Adds the specified component to the end of this list.
240 	 * 
241 	 * @param obj
242 	 *          the component to be added
243 	 * @see List#add(Object)
244 	 */
245 	public void add(T obj) {
246 		int index = delegate.size();
247 		delegate.add(obj);
248 		fireIntervalAdded(this, index, index);
249 		updateAmount();
250 	}
251 
252 	/***
253 	 * Removes the first (lowest-indexed) occurrence of the argument from this
254 	 * list.
255 	 * 
256 	 * @param obj
257 	 *          the component to be removed
258 	 * @return <code>true</code> if the argument was a component of this list;
259 	 *         <code>false</code> otherwise
260 	 * @see List#remove(Object)
261 	 */
262 	public boolean remove(T obj) {
263 		int index = indexOf(obj);
264 		boolean rv = delegate.remove(obj);
265 		if (index >= 0) {
266 			fireIntervalRemoved(this, index, index);
267 		}
268 		updateAmount();
269 		return rv;
270 	}
271 
272 	/***
273 	 * Returns a string that displays and identifies this object's properties.
274 	 * 
275 	 * @return a String representation of this object
276 	 */
277 	@Override
278 	public String toString() {
279 		return delegate.toString();
280 	}
281 
282 	/***
283 	 * Returns the element at the specified position in this list.
284 	 * <p>
285 	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index is out
286 	 * of range (<code>index &lt; 0 || index &gt;= size()</code>).
287 	 * 
288 	 * @param index
289 	 *          index of element to return
290 	 * @return the object.
291 	 */
292 	public T get(int index) {
293 		return delegate.get(index);
294 	}
295 
296 	/***
297 	 * Replaces the element at the specified position in this list with the
298 	 * specified element.
299 	 * <p>
300 	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index is out
301 	 * of range (<code>index &lt; 0 || index &gt;= size()</code>).
302 	 * 
303 	 * @param index
304 	 *          index of element to replace
305 	 * @param element
306 	 *          element to be stored at the specified position
307 	 * @return the element previously at the specified position
308 	 */
309 	public T set(int index, T element) {
310 		T rv = delegate.get(index);
311 		delegate.set(index, element);
312 		fireContentsChanged(this, index, index);
313 		updateAmount();
314 		return rv;
315 	}
316 
317 	/***
318 	 * Inserts the specified object as a component in this list at the specified
319 	 * <code>index</code>.
320 	 * <p>
321 	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index is
322 	 * invalid. <blockquote> <b>Note:</b> Although this method is not deprecated,
323 	 * the preferred method to use is <code>add(int,Object)</code>, which
324 	 * implements the <code>List</code> interface defined in the 1.2 Collections
325 	 * framework. </blockquote>
326 	 * 
327 	 * @param obj
328 	 *          the component to insert
329 	 * @param index
330 	 *          where to insert the new component
331 	 * @exception ArrayIndexOutOfBoundsException
332 	 *              if the index was invalid
333 	 */
334 	public void insertElementAt(T obj, int index) {
335 		delegate.insertElementAt(obj, index);
336 		fireIntervalAdded(this, index, index);
337 		updateAmount();
338 	}
339 
340 	/***
341 	 * Removes the element at the specified position in this list. Returns the
342 	 * element that was removed from the list.
343 	 * <p>
344 	 * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index is out
345 	 * of range (<code>index &lt; 0 || index &gt;= size()</code>).
346 	 * 
347 	 * @param index
348 	 *          the index of the element to removed
349 	 * @return the removed object.
350 	 */
351 	public T remove(int index) {
352 		T rv = delegate.get(index);
353 		delegate.remove(index);
354 		fireIntervalRemoved(this, index, index);
355 		updateAmount();
356 		return rv;
357 	}
358 
359 	/***
360 	 * Removes all of the elements from this list. The list will be empty after
361 	 * this call returns (unless it throws an exception).
362 	 */
363 	public void clear() {
364 		int index1 = delegate.size() - 1;
365 		delegate.clear();
366 		if (index1 >= 0) {
367 			fireIntervalRemoved(this, 0, index1);
368 		}
369 		updateAmount();
370 	}
371 
372 	/***
373 	 * Returns an array containing all of the elements in this list in proper
374 	 * sequence (from first to last element).
375 	 * <p>
376 	 * The returned array will be "safe" in that no references to it are
377 	 * maintained by this list. (In other words, this method must allocate a new
378 	 * array even if this list is backed by an array). The caller is thus free to
379 	 * modify the returned array.
380 	 * <p>
381 	 * This method acts as bridge between array-based and collection-based APIs.
382 	 * 
383 	 * @return an array containing all of the elements in this list in proper
384 	 *         sequence
385 	 * @see Arrays#asList(Object[])
386 	 */
387 	@SuppressWarnings("unchecked")
388 	public T[] toArray() {
389 		return (T[]) delegate.toArray();
390 	}
391 
392 	/***
393 	 * Removes from this Vector all of its elements that are contained in the
394 	 * specified Collection.
395 	 * 
396 	 * @param c
397 	 *          a collection of elements to be removed from the Vector
398 	 * @return true if this Vector changed as a result of the call
399 	 * @throws ClassCastException
400 	 *           if the types of one or more elements in this vector are
401 	 *           incompatible with the specified collection (optional)
402 	 * @throws NullPointerException
403 	 *           if this vector contains one or more null elements and the
404 	 *           specified collection does not support null elements (optional),
405 	 *           or if the specified collection is null
406 	 * @since 1.2
407 	 */
408 	public boolean removeAll(Collection<T> c) {
409 		removedDelegate.addAll(c);
410 		delegate.removeAll(c);
411 		updateAmount();
412 		return true;
413 	}
414 
415 	/***
416 	 * Appends all of the elements in the specified Collection to the end of this
417 	 * Vector, in the order that they are returned by the specified Collection's
418 	 * Iterator. The behavior of this operation is undefined if the specified
419 	 * Collection is modified while the operation is in progress. (This implies
420 	 * that the behavior of this call is undefined if the specified Collection is
421 	 * this Vector, and this Vector is nonempty.)
422 	 * 
423 	 * @param c
424 	 *          elements to be inserted into this Vector
425 	 * @return {@code true} if this Vector changed as a result of the call
426 	 * @throws NullPointerException
427 	 *           if the specified collection is null
428 	 * @since 1.2
429 	 */
430 	public boolean addAll(Collection<T> c) {
431 		removedDelegate.removeAll(c);
432 		delegate.addAll(c);
433 		Collections.sort(delegate);
434 		updateAmount();
435 		return true;
436 	}
437 
438 	/***
439 	 * Update the amount in the attached label.
440 	 */
441 	private void updateAmount() {
442 		int rightAmount;
443 		if (deepSum) {
444 			rightAmount = 0;
445 			for (MCardCompare card : delegate) {
446 				rightAmount += card.getAmount();
447 			}
448 		} else {
449 			rightAmount = removedDelegate.size() + delegate.size();
450 		}
451 		linkedAmount.setText("<html>" + delegate.size() + "/" + rightAmount);
452 		fireIntervalAdded(this, 0, 0);
453 	}
454 }