1 /*
2  * $RCSfile: WakeupIndexedList.java,v $
3  *
4  * Copyright 2001-2008 Sun Microsystems, Inc.  All Rights Reserved.
5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6  *
7  * This code is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 only, as
9  * published by the Free Software Foundation.  Sun designates this
10  * particular file as subject to the "Classpath" exception as provided
11  * by Sun in the LICENSE file that accompanied this code.
12  *
13  * This code is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16  * version 2 for more details (a copy is included in the LICENSE file that
17  * accompanied this code).
18  *
19  * You should have received a copy of the GNU General Public License version
20  * 2 along with this work; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
24  * CA 95054 USA or visit www.sun.com if you need additional information or
25  * have any questions.
26  *
27  * $Revision: 1.7 $
28  * $Date: 2008/02/28 20:17:33 $
29  * $State: Exp $
30  */
31 
32 package javax.media.j3d;
33 
34 /**
35  * A strongly type unorder indexed array list.
36  * All operations remove(WakeupCondition), add(WakeupCondition),
37  * contains(WakeupCondition) etc. take O(1) time.
38  * The class is designed to optimize speed. So many reductance
39  * procedures call and range check as found in ArrayList are
40  * removed.
41  *
42  * <p>
43  * Use the following code to iterate through an array.
44  *
45  * <pre>
46  *  WakeupIndexedList  list = new WakeupIndexedList(YourClass.class);
47  *  // add element here
48  *
49  *  YourClass[] arr = (YourClass []) list.toArray();
50  *  int size = list.arraySize();
51  *  for (int i=0; i < size; i++) {
52  *      YourClass obj = arr[i];
53  *      ....
54  *  }
55  * </pre>
56  *
57  * <p>
58  * Note:
59  * <ul>
60  *       1) The array return is a copied of internal array.<br>
61  *       2) Don't use arr.length , use list.arraySize();<br>
62  *       3) No need to do casting for individual element as in
63  *          ArrayList.<br>
64  *       4) WakeupIndexedList is thread safe.<br>
65  *       5) Object implement this interface MUST initialize the index
66  *          to -1.<br>
67  * </ul>
68  *
69  * <p>
70  * Limitation:
71  * <ul>
72  *       - Same element can't add in two different WakeupIndexedList<br>
73  *       - Order of WakeupCondition is not important<br>
74  *       - Can't modify the clone() copy.<br>
75  *       - Object can't be null<br>
76  * </ul>
77  */
78 
79 class WakeupIndexedList implements Cloneable, java.io.Serializable  {
80 
81     // XXXX: set to false when release
82     final static boolean debug = false;
83 
84     /**
85      * The array buffer into which the elements of the ArrayList are stored.
86      * The capacity of the ArrayList is the length of this array buffer.
87      *
88      * It is non-private to enable compiler do inlining for get(),
89      * set(), remove() when -O flag turn on.
90      */
91     transient WakeupCondition elementData[];
92 
93     /**
94      * Clone copy of elementData return by toArray(true);
95      */
96     transient Object cloneData[];
97     // size of the above clone objec.
98     transient int cloneSize;
99 
100     transient boolean isDirty = true;
101 
102     /**
103      * Component Type of individual array element entry
104      */
105     Class componentType;
106 
107     /**
108      * The size of the ArrayList (the number of elements it contains).
109      *
110      * We make it non-private to enable compiler do inlining for
111      * getSize() when -O flag turn on.
112      */
113     int size;
114 
115     int listType;
116 
117     // Current VirtualUniverse using this structure
118     VirtualUniverse univ;
119 
120     /**
121      * Constructs an empty list with the specified initial capacity.
122      * and the class data Type
123      *
124      * @param   initialCapacity   the initial capacity of the list.
125      * @param   componentType     class type of element in the list.
126      */
WakeupIndexedList(int initialCapacity, Class componentType, int listType, VirtualUniverse univ)127     WakeupIndexedList(int initialCapacity, Class componentType,
128 		      int listType, VirtualUniverse univ) {
129 	this.componentType = componentType;
130 	this.elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
131 						componentType, initialCapacity);
132 	this.listType = listType;
133 	this.univ = univ;
134     }
135 
136     /**
137      * Constructs an empty list.
138      * @param   componentType     class type of element in the list.
139      */
WakeupIndexedList(Class componentType, int listType, VirtualUniverse univ)140     WakeupIndexedList(Class componentType, int listType,
141 		      VirtualUniverse univ) {
142 	this(10, componentType, listType, univ);
143      }
144 
145 
146     /**
147      * Constructs an empty list with the specified initial capacity.
148      *
149      * @param   initialCapacity   the initial capacity of the list.
150      */
WakeupIndexedList(int initialCapacity, int listType, VirtualUniverse univ)151     WakeupIndexedList(int initialCapacity, int listType,
152 		      VirtualUniverse univ) {
153 	this(initialCapacity, WakeupCondition.class, listType, univ);
154     }
155 
156 
157     /**
158      * Constructs an empty list.
159      * componentType default to Object.
160      */
WakeupIndexedList(int listType, VirtualUniverse univ)161     WakeupIndexedList(int listType, VirtualUniverse univ) {
162 	this(10, WakeupCondition.class, listType, univ);
163     }
164 
165 
166     /**
167      * Initialize all indexes to -1
168      */
init(WakeupCondition obj, int len)169     final static void init(WakeupCondition obj, int len) {
170 	obj.listIdx = new int[2][len];
171 
172 	for (int i=0; i < len; i++) {
173 	    obj.listIdx[0][i] = -1;
174 	    obj.listIdx[1][i] = -1;
175 	}
176     }
177 
178     /**
179      * Returns the number of elements in this list.
180      *
181      * @return  the number of elements in this list.
182      */
size()183     final int size() {
184 	return size;
185     }
186 
187     /**
188      * Returns the size of entry use in toArray() number of elements
189      * in this list.
190      *
191      * @return  the number of elements in this list.
192      */
arraySize()193     final int arraySize() {
194 	return cloneSize;
195     }
196 
197     /**
198      * Tests if this list has no elements.
199      *
200      * @return  <tt>true</tt> if this list has no elements;
201      *          <tt>false</tt> otherwise.
202      */
isEmpty()203     final boolean isEmpty() {
204 	return size == 0;
205     }
206 
207     /**
208      * Returns <tt>true</tt> if this list contains the specified element.
209      *
210      * @param o element whose presence in this List is to be tested.
211      */
contains(WakeupCondition o)212     synchronized final boolean contains(WakeupCondition o) {
213 	return (o.listIdx[o.behav.getIdxUsed(univ)][listType] >= 0);
214     }
215 
216 
217     /**
218      * Searches for the last occurence of the given argument, testing
219      * for equality using the <tt>equals</tt> method.
220      *
221      * @param   o   an object.
222      * @return  the index of the first occurrence of the argument in this
223      *          list; returns <tt>-1</tt> if the object is not found.
224      * @see     Object#equals(Object)
225      */
indexOf(WakeupCondition o)226     synchronized final int indexOf(WakeupCondition o) {
227 	return o.listIdx[o.behav.getIdxUsed(univ)][listType];
228     }
229 
230     /**
231      * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
232      * elements themselves are not copied.)
233      *
234      * @return  a clone of this <tt>ArrayList</tt> instance.
235      */
clone()236     synchronized protected final Object clone() {
237 	try {
238 	    WakeupIndexedList v = (WakeupIndexedList)super.clone();
239 	    v.elementData =  (WakeupCondition[])java.lang.reflect.Array.newInstance(
240 						   componentType, size);
241 	    System.arraycopy(elementData, 0, v.elementData, 0, size);
242 	    isDirty = true; // can't use the old cloneData reference
243 	    return v;
244 	} catch (CloneNotSupportedException e) {
245 	    // this shouldn't happen, since we are Cloneable
246 	    throw new InternalError();
247 	}
248     }
249 
250 
251     /**
252      * Returns an array containing all of the elements in this list.
253      * The size of the array may longer than the actual size. Use
254      * arraySize() to retrieve the size.
255      * The array return is a copied of internal array. if copy
256      * is true.
257      *
258      * @return an array containing all of the elements in this list
259      */
toArray(boolean copy)260     synchronized final Object[] toArray(boolean copy) {
261 	if (copy) {
262 	    if (isDirty) {
263 		if ((cloneData == null) || cloneData.length < size) {
264 		    cloneData = (Object[])java.lang.reflect.Array.newInstance(
265 									      componentType, size);
266 		}
267 		System.arraycopy(elementData, 0, cloneData, 0, size);
268 		cloneSize = size;
269 		isDirty = false;
270 	    }
271 	    return cloneData;
272 	} else {
273 	    cloneSize = size;
274 	    return elementData;
275 	}
276 
277     }
278 
279     /**
280      * Returns an array containing all of the elements in this list.
281      * The size of the array may longer than the actual size. Use
282      * arraySize() to retrieve the size.
283      * The array return is a copied of internal array. So another
284      * thread can continue add/delete the current list. However,
285      * it should be noticed that two call to toArray() may return
286      * the same copy.
287      *
288      * @return an array containing all of the elements in this list
289      */
toArray()290     synchronized final Object[] toArray() {
291 	return toArray(true);
292     }
293 
294 
295     /**
296      * Returns an array containing elements starting from startElement
297      * all of the elements in this list. A new array of exact size
298      * is always allocated.
299      *
300      * @param startElement starting element to copy
301      *
302      * @return an array containing elements starting from
303      *         startElement, null if element not found.
304      *
305      */
toArray(WakeupCondition startElement)306     synchronized final Object[] toArray(WakeupCondition startElement) {
307 	int idx = indexOf(startElement);
308 	if (idx < 0) {
309 	    return  (Object[])java.lang.reflect.Array.newInstance(componentType, 0);
310 	}
311 
312 	int s = size - idx;
313 	Object data[] = (Object[])java.lang.reflect.Array.newInstance(componentType, s);
314 	System.arraycopy(elementData, idx, data, 0, s);
315 	return data;
316     }
317 
318     /**
319      * Trims the capacity of this <tt>ArrayList</tt> instance to be the
320      * list's current size.  An application can use this operation to minimize
321      * the storage of an <tt>ArrayList</tt> instance.
322      */
trimToSize()323     synchronized final void trimToSize() {
324 	if (elementData.length > size) {
325 	    Object oldData[] = elementData;
326 	    elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
327 						 componentType,
328 						 size);
329 	    System.arraycopy(oldData, 0, elementData, 0, size);
330 	}
331     }
332 
333 
334     // Positional Access Operations
335 
336     /**
337      * Returns the element at the specified position in this list.
338      *
339      * @param  index index of element to return.
340      * @return the element at the specified position in this list.
341      * @throws    IndexOutOfBoundsException if index is out of range <tt>(index
342      * 		  &lt; 0 || index &gt;= size())</tt>.
343      */
get(int index)344     synchronized final Object get(int index) {
345 	return elementData[index];
346     }
347 
348     /**
349      * Replaces the element at the specified position in this list with
350      * the specified element.
351      *
352      * @param index index of element to replace.
353      * @param o element to be stored at the specified position.
354      * @return the element previously at the specified position.
355      * @throws    IndexOutOfBoundsException if index out of range
356      *		  <tt>(index &lt; 0 || index &gt;= size())</tt>.
357      */
set(int index, WakeupCondition o)358     synchronized final void set(int index, WakeupCondition o) {
359 
360 	WakeupCondition oldElm = elementData[index];
361 	if (oldElm != null) {
362 	    oldElm.listIdx[oldElm.behav.getIdxUsed(univ)][listType] = -1;
363 	}
364 	elementData[index] = o;
365 
366 	int univIdx = o.behav.getIdxUsed(univ);
367 
368 	if (debug) {
369 	    if (o.listIdx[univIdx][listType] != -1) {
370 		System.err.println("Illegal use of UnorderIndexedList idx in set " +
371 				   o.listIdx[univIdx][listType]);
372 		Thread.dumpStack();
373 	    }
374 	}
375 
376 	o.listIdx[univIdx][listType] = index;
377 	isDirty = true;
378     }
379 
380     /**
381      * Appends the specified element to the end of this list.
382      * It is the user responsible to ensure that the element add is of
383      * the same type as array componentType.
384      *
385      * @param o element to be appended to this list.
386      */
add(WakeupCondition o)387     synchronized final void add(WakeupCondition o) {
388 	if (elementData.length == size) {
389 	    WakeupCondition oldData[] = elementData;
390 	    elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
391 						 componentType,
392 						 (size << 1));
393 	    System.arraycopy(oldData, 0, elementData, 0, size);
394 
395 	}
396 
397 	int univIdx = o.behav.getIdxUsed(univ);
398 	//	System.err.println(this + " add " + o + " univ " + univIdx);
399 	if (debug) {
400 	    int idx = o.listIdx[univIdx][listType];
401 	    if (idx >= 0) {
402 		if (elementData[idx] != o) {
403 		    System.err.println("Illegal use of UnorderIndexedList idx in add " + idx);
404 		    Thread.dumpStack();
405 		}
406 	    }
407 	}
408 
409 	int idx = size++;
410 	elementData[idx] = o;
411 	o.listIdx[univIdx][listType] = idx;
412 	isDirty = true;
413     }
414 
415 
416     /**
417      * Removes the element at the specified position in this list.
418      * Replace the removed element by the last one.
419      *
420      * @param index the index of the element to removed.
421      * @throws    IndexOutOfBoundsException if index out of range <tt>(index
422      * 		  &lt; 0 || index &gt;= size())</tt>.
423      */
remove(int index)424     synchronized final void remove(int index) {
425 	WakeupCondition elm = elementData[index];
426 	int univIdx = elm.behav.getIdxUsed(univ);
427 
428 	if (debug) {
429 	    if (elm.listIdx[univIdx][listType] != index) {
430 		System.err.println("Inconsistent idx in remove, expect " + index +
431 				   " actual " + elm.listIdx[univIdx][listType]);
432 		Thread.dumpStack();
433 	    }
434 	}
435 
436 	elm.listIdx[univIdx][listType] = -1;
437 	size--;
438 	if (index != size) {
439 	    elm = elementData[size];
440 	    elm.listIdx[univIdx][listType] = index;
441 	    elementData[index] = elm;
442 	}
443 	elementData[size] = null;
444 	isDirty = true;
445 	/*
446 	if ((cloneData != null) && (index < cloneData.length)) {
447 	    cloneData[index] = null; // for gc
448 	}
449 	*/
450     }
451 
452 
453    /**
454      * Removes the element at the last position in this list.
455      * @return    The element remove
456      * @throws    IndexOutOfBoundsException if array is empty
457      */
removeLastElement()458     synchronized final Object removeLastElement() {
459 	WakeupCondition elm = elementData[--size];
460 	elementData[size] = null;
461 	elm.listIdx[elm.behav.getIdxUsed(univ)][listType] = -1;
462 	isDirty = true;
463 	/*
464 	if ((cloneData != null) && (size < cloneData.length)) {
465 	    cloneData[size] = null; // for gc
466 	}
467 	*/
468 	return elm;
469     }
470 
471 
472     /**
473      * Removes the specified element in this list.
474      * Replace the removed element by the last one.
475      *
476      * @param o   the element to removed.
477      * @return    true if object remove
478      * @throws    IndexOutOfBoundsException if index out of range <tt>(index
479      * 		  &lt; 0 || index &gt;= size())</tt>.
480      */
remove(WakeupCondition o)481     synchronized final boolean remove(WakeupCondition o) {
482 	int univIdx = o.behav.getIdxUsed(univ);
483 	int idx = o.listIdx[univIdx][listType];
484 
485 	//	System.err.println(this + " remove " + o + " univ " + univIdx);
486 
487 	if (idx >= 0) {
488 	    // Object in the container
489 	    if (debug) {
490 		if (o != elementData[idx]) {
491 		    System.err.println(" Illegal use of UnorderIndexedList in remove expect " + o + " actual " + elementData[idx] + " idx = " + idx);
492 		    Thread.dumpStack();
493 		}
494 	    }
495 	    size--;
496 	    if (idx != size) {
497 		WakeupCondition elm = elementData[size];
498 		elementData[idx] = elm;
499 		elm.listIdx[elm.behav.getIdxUsed(univ)][listType] = idx;
500 	    }
501 	    elementData[size] = null;
502 	    o.listIdx[univIdx][listType] = -1;
503 	    isDirty = true;
504 	    return true;
505 	}
506 	return false;
507     }
508 
509 
510     /**
511      * Removes all of the elements from this list.  The list will
512      * be empty after this call returns.
513      */
clear()514     synchronized final void clear() {
515 	WakeupCondition o;
516 
517 	for (int i = size-1; i >= 0; i--) {
518 	    o = elementData[i];
519 	    o.listIdx[o.behav.getIdxUsed(univ)][listType] = -1;
520 	    elementData[i] = null; 	// Let gc do its work
521 	}
522 
523 	size = 0;
524 	isDirty = true;
525     }
526 
clearMirror()527     synchronized final void clearMirror() {
528 	if (cloneData != null) {
529 	    for (int i = cloneData.length-1; i >= 0; i--) {
530 		// don't set index to -1 since the original
531 		// copy is using this.
532 		cloneData[i] = null; 	// Let gc do its work
533 	    }
534 	}
535 	cloneSize = 0;
536 	isDirty = true;
537     }
538 
getComponentType()539     final Class getComponentType() {
540 	return componentType;
541     }
542 
toString()543     synchronized public String toString() {
544 	StringBuffer sb = new StringBuffer(hashCode() + " Size = " + size + "[");
545 	int len = size-1;
546 	Object obj;
547 
548 	for (int i=0; i < size; i++) {
549 	    obj = elementData[i];
550 	    if (obj != null) {
551 		sb.append(elementData[i].toString());
552 	    } else {
553 		sb.append("NULL");
554 	    }
555 	    if (i != len) {
556 		sb.append(", ");
557 	    }
558 	}
559 	sb.append("]");
560 	return sb.toString();
561     }
562 
563     /**
564      * Save the state of the <tt>ArrayList</tt> instance to a stream (that
565      * is, serialize it).
566      *
567      * @serialData The length of the array backing the <tt>ArrayList</tt>
568      *             instance is emitted (int), followed by all of its elements
569      *             (each an <tt>Object</tt>) in the proper order.
570      */
writeObject(java.io.ObjectOutputStream s)571     private synchronized void writeObject(java.io.ObjectOutputStream s)
572         throws java.io.IOException{
573 	// Write out element count, and any hidden stuff
574 	s.defaultWriteObject();
575 
576         // Write out array length
577         s.writeInt(elementData.length);
578 
579 	// Write out all elements in the proper order.
580 	for (int i=0; i<size; i++)
581             s.writeObject(elementData[i]);
582 
583     }
584 
585     /**
586      * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
587      * deserialize it).
588      */
readObject(java.io.ObjectInputStream s)589     private synchronized void readObject(java.io.ObjectInputStream s)
590         throws java.io.IOException, ClassNotFoundException {
591 	// Read in size, and any hidden stuff
592 	s.defaultReadObject();
593 
594         // Read in array length and allocate array
595         int arrayLength = s.readInt();
596 	elementData = (WakeupCondition[])java.lang.reflect.Array.newInstance(
597 						   componentType, arrayLength);
598 
599 	// Read in all elements in the proper order.
600 	for (int i=0; i<size; i++)
601             elementData[i] = (WakeupCondition) s.readObject();
602     }
603 }
604