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 * < 0 || index >= 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 < 0 || index >= 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 * < 0 || index >= 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 * < 0 || index >= 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