1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package com.sun.star.lib.uno.helper; 25 import java.util.Iterator; 26 import java.util.ListIterator; 27 import java.util.NoSuchElementException; 28 import java.util.Collection; 29 import com.sun.star.lang.EventObject; 30 import com.sun.star.lang.XEventListener; 31 import com.sun.star.uno.UnoRuntime; 32 33 /** 34 * This class is a container for interfaces. 35 * 36 * It is intended to be used as storage for UNO interface of a specific type. 37 * The client has to ensure that the container contains only elements of the same 38 * type. If one needs to store different types, then one uses OMultiTypeInterfaceContainer. 39 * When the client calls disposeAndClear, the contained objects are queried for 40 * com.sun.star.lang.XEventListener and disposing is called. Afterwards 41 * the list cannot be used anymore. 42 * 43 * This list does not allow null values. 44 * All methods are thread-safe. The same holds true for 45 * iterators, issued by this class. Several iterators can exist at the same time and can also 46 * be modified (java.util.ListIterator.add, java.util.ListIterator.remove etc.). To make this work, 47 * the InterfaceContainer provides the iterators with copys of the list's data. 48 * The add and remove calls on the iterator modify the data in the iterator's list as well as 49 * in InterfaceContainer. Modification on InterfaceContainer, however, are not 50 * synchronized with existing iterators. For example 51 * <pre> 52 * InterfaceContainer cont= new InterfaceContainer(); 53 * ListIterator it= cont.listIterator(); 54 * 55 * cont.add( someInterface); 56 * // one cannot obtain someInterface through iterator it, 57 * // instead get a new iterator 58 * it= cont.listIterator(); 59 * // it now keeps a fresh copy of cont and hence contains someInterface 60 * 61 * // Adding an interface on the iterator will cause the interface also to be added 62 * // to InterfaceContainer 63 * it.add( someOtherInterface); 64 * // someOtherInterface is now in it and cont 65 * ListIterator it2= cont.listIterator(); 66 * //someOtherInterface can also be obtained by all newly created iterators, e.g. it2. 67 * </pre> 68 * 69 * The add and remove methods of an iterator work on a particular location within a list, 70 * dependent on what the value of the iterator's cursor is. After the call the value at the 71 * appropriate position has been modified. Since the iterator received a copy of InterfaceContainer's 72 * data, InterfaceContainer may have been modified (by List methods or through other iterators). 73 * Therefore both data sets may not contain the same elements anymore. Consequently, a List method 74 * that modifies data, does not modify InterfaceContainer's data at a certain index 75 * (according to the iterators cursor). Instead, new elements are added at the end of list. When 76 * Iterator.remove is called, then the first occurrence of that element in InterfaceContainer 77 * is removed. 78 * ListIterator.set is not supported. 79 * 80 * A lot of methods resemble those of the to java.util.List interface, although 81 * this class does not implement it. However, the list iterators returned, for example by 82 * the listIterator method implement the java.util.ListIterator interface. 83 * Implementing the List interface would mean to support all none - optional methods as 84 * prescribed by the interface declaration. Among those is the subList method which returns 85 * a range of values of the list's data wrapped in a List implementation. Changes to the sub 86 * list have to cause changes in the main list. This is a problem, since this class is to be 87 * used in a multi-threaded environment. The sub list could work on a copy as the iterators 88 * do, but all the functions which work on an given index could not be properly supported. 89 * Unfortunately, the List interface documentation states that all optional methods implemented 90 * by the list have to be implemented in the sub list. That would mean to do without all those 91 * critical methods, although they might work well in the "main list" (as opposed to sub list). 92 */ 93 public class InterfaceContainer implements Cloneable 94 { 95 final boolean DEBUG= false; 96 /** 97 * The array buffer into which the elements of the ArrayList are stored. 98 * The capacity of the ArrayList is the length of this array buffer. 99 */ 100 Object elementData[]; 101 102 /** 103 * The size of the ArrayList (the number of elements it contains). 104 */ 105 private int size; 106 107 108 //private ArrayList data= new ArrayList(); 109 /** Creates a new instance of InterfaceContainer */ 110 public InterfaceContainer() 111 { 112 this(10); 113 } 114 /** 115 * Constructs an empty list with the specified initial capacity. 116 * 117 * @param initialCapacity the initial capacity of the list. 118 * @exception IllegalArgumentException if the specified initial capacity 119 * is negative 120 */ 121 public InterfaceContainer(int initialCapacity) 122 { 123 if (initialCapacity < 0) 124 throw new java.lang.IllegalArgumentException("Illegal Capacity: "+ 125 initialCapacity); 126 this.elementData = new Object[initialCapacity]; 127 } 128 129 /** 130 * Trims the capacity of this <tt>ArrayList</tt> instance to be the 131 * list's current size. An application can use this operation to minimize 132 * the storage of an <tt>ArrayList</tt> instance. 133 */ 134 synchronized public void trimToSize() 135 { 136 int oldCapacity = elementData.length; 137 if (size < oldCapacity) 138 { 139 Object oldData[] = elementData; 140 elementData = new Object[size]; 141 System.arraycopy(oldData, 0, elementData, 0, size); 142 } 143 } 144 145 /** 146 * Increases the capacity of this <tt>ArrayList</tt> instance, if 147 * necessary, to ensure that it can hold at least the number of elements 148 * specified by the minimum capacity argument. 149 * 150 * @param minCapacity the desired minimum capacity. 151 */ 152 synchronized public void ensureCapacity(int minCapacity) 153 { 154 int oldCapacity = elementData.length; 155 if (minCapacity > oldCapacity) 156 { 157 Object oldData[] = elementData; 158 int newCapacity = (oldCapacity * 3)/2 + 1; 159 if (newCapacity < minCapacity) 160 newCapacity = minCapacity; 161 elementData = new Object[newCapacity]; 162 System.arraycopy(oldData, 0, elementData, 0, size); 163 } 164 } 165 166 /** 167 * Appends the specified element to the end of this list. 168 * 169 * @param o element to be appended to this list. 170 * @return <tt>true</tt> (as per the general contract of Collection.add). 171 */ 172 synchronized public boolean add(Object o) 173 { 174 boolean ret= false; 175 if (elementData != null && o != null) 176 { 177 ensureCapacity(size + 1); // Increments modCount!! 178 elementData[size++] = o; 179 ret= true; 180 } 181 return ret; 182 } 183 184 /** 185 * Inserts the specified element at the specified position in this 186 * list. Shifts the element currently at that position (if any) and 187 * any subsequent elements to the right (adds one to their indices). 188 * 189 * @param index index at which the specified element is to be inserted. 190 * @param element element to be inserted. 191 * @throws IndexOutOfBoundsException if index is out of range 192 * <tt>(index < 0 || index > size())</tt>. 193 */ 194 synchronized public void add(int index, Object element) 195 { 196 if (elementData != null && element != null) 197 { 198 if (index > size || index < 0) 199 throw new IndexOutOfBoundsException( 200 "Index: "+index+", Size: "+size); 201 202 ensureCapacity(size+1); 203 System.arraycopy(elementData, index, elementData, index + 1, 204 size - index); 205 elementData[index] = element; 206 size++; 207 } 208 } 209 210 211 /** 212 * Appends all of the elements in the specified Collection to the end of 213 * this list, in the order that they are returned by the 214 * specified Collection's Iterator. The behavior of this operation is 215 * undefined if the specified Collection is modified while the operation 216 * is in progress. (This implies that the behavior of this call is 217 * undefined if the specified Collection is this list, and this 218 * list is nonempty.) 219 * 220 * @param c the elements to be inserted into this list. 221 * @throws IndexOutOfBoundsException if index out of range <tt>(index 222 * < 0 || index > size())</tt>. 223 */ 224 synchronized public boolean addAll(Collection c) 225 { 226 int numNew = c.size(); 227 ensureCapacity(size + numNew); 228 229 Iterator e = c.iterator(); 230 for (int i=0; i<numNew; i++) 231 { 232 Object o= e.next(); 233 if (o != null) 234 elementData[size++] = o; 235 } 236 return numNew != 0; 237 } 238 /** 239 * Inserts all of the elements in the specified Collection into this 240 * list, starting at the specified position. Shifts the element 241 * currently at that position (if any) and any subsequent elements to 242 * the right (increases their indices). The new elements will appear 243 * in the list in the order that they are returned by the 244 * specified Collection's iterator. 245 * 246 * @param index index at which to insert first element 247 * from the specified collection. 248 * @param c elements to be inserted into this list. 249 * @throws IndexOutOfBoundsException if index out of range <tt>(index 250 * < 0 || index > size())</tt>. 251 */ 252 synchronized public boolean addAll(int index, Collection c) 253 { 254 boolean ret= false; 255 if (elementData != null) 256 { 257 if (index > size || index < 0) 258 throw new IndexOutOfBoundsException( 259 "Index: "+index+", Size: "+size); 260 // only add the non-null elements 261 int sizeCol= c.size(); 262 Object[] arColl= new Object[sizeCol]; 263 Iterator icol= c.iterator(); 264 int curIndex= 0; 265 for (int i=0; i < sizeCol; i++) 266 { 267 Object o= icol.next(); 268 if (o != null) 269 arColl[curIndex++]= o; 270 } 271 int numNew = curIndex; 272 ensureCapacity(size + numNew); // Increments modCount!! 273 274 int numMoved = size - index; 275 if (numMoved > 0) 276 System.arraycopy(elementData, index, elementData, index + numNew, 277 numMoved); 278 279 for (int i=0; i<numNew; i++) 280 { 281 elementData[index++]= arColl[i]; 282 } 283 size += numNew; 284 ret= numNew != 0; 285 } 286 return ret; 287 } 288 289 /** 290 * Removes all of the elements from this list. The list will 291 * be empty after this call returns. 292 */ 293 synchronized public void clear() 294 { 295 if (elementData != null) 296 { 297 // Let gc do its work 298 for (int i = 0; i < size; i++) 299 elementData[i] = null; 300 301 size = 0; 302 } 303 } 304 /** 305 * Returns <tt>true</tt> if this list contains the specified element. 306 * 307 * @param elem element whose presence in this List is to be tested. 308 */ 309 synchronized public boolean contains(Object elem) 310 { 311 return indexOf(elem) >= 0; 312 } 313 314 synchronized public boolean containsAll(Collection collection) 315 { 316 boolean retVal= true; 317 if (elementData != null && collection != null) 318 { 319 Iterator it= collection.iterator(); 320 while (it.hasNext()) 321 { 322 Object obj= it.next(); 323 if (false == contains(obj)) 324 { 325 retVal= false; 326 break; 327 } 328 } 329 } 330 return retVal; 331 } 332 /** 333 * Returns the element at the specified position in this list. 334 * 335 * @param index index of element to return. 336 * @return the element at the specified position in this list. 337 * @throws IndexOutOfBoundsException if index is out of range <tt>(index 338 * < 0 || index >= size())</tt>. 339 */ 340 synchronized public Object get(int index) 341 { 342 if (elementData != null) 343 { 344 RangeCheck(index); 345 return elementData[index]; 346 } 347 return null; 348 } 349 350 /** 351 * Searches for the first occurrence of the given argument, testing 352 * for equality using the <tt>equals</tt> method. 353 * 354 * @param elem an object. 355 * @return the index of the first occurrence of the argument in this 356 * list; returns <tt>-1</tt> if the object is not found. 357 * @see Object#equals(Object) 358 */ 359 synchronized public int indexOf(Object elem) 360 { 361 int index= -1; 362 if (elementData != null && elem != null) 363 { 364 for (int i = 0; i < size; i++) 365 { 366 if (elem == elementData[i]) 367 { 368 index= i; 369 break; 370 } 371 } 372 373 if (index == -1) 374 { 375 for (int i = 0; i < size; i++) 376 { 377 if (UnoRuntime.areSame(elem, elementData[i])) 378 { 379 index= i; 380 break; 381 } 382 } 383 } 384 } 385 return index; 386 } 387 /** 388 * Tests if this list has no elements. 389 * 390 * @return <tt>true</tt> if this list has no elements; 391 * <tt>false</tt> otherwise. 392 */ 393 synchronized public boolean isEmpty() 394 { 395 return size == 0; 396 } 397 398 synchronized public Iterator iterator() 399 { 400 if (elementData != null) 401 { 402 InterfaceContainer aCopy= (InterfaceContainer) clone(); 403 return new Itr(aCopy); 404 } 405 return null; 406 } 407 /** 408 * Returns the index of the last occurrence of the specified object in 409 * this list. 410 * 411 * @param elem the desired element. 412 * @return the index of the last occurrence of the specified object in 413 * this list; returns -1 if the object is not found. 414 */ 415 synchronized public int lastIndexOf(Object elem) 416 { 417 int index= -1; 418 if (elementData != null && elem != null) 419 { 420 for (int i = size-1; i >= 0; i--) 421 { 422 if (elem == elementData[i]) 423 { 424 index= i; 425 break; 426 } 427 } 428 if (index == -1) 429 { 430 for (int i = size-1; i >= 0; i--) 431 { 432 if (UnoRuntime.areSame(elem, elementData[i])) 433 { 434 index= i; 435 break; 436 } 437 } 438 } 439 } 440 return index; 441 } 442 443 /** 444 * Returns a shallow copy of this <tt>ArrayList</tt> instance. The contained 445 * references are copied but the objects not. 446 * 447 * @return a clone of this <tt>List</tt> instance. 448 */ 449 synchronized public Object clone() 450 { 451 Object ret= null; 452 if (elementData != null) 453 { 454 InterfaceContainer cont= new InterfaceContainer(); 455 cont.elementData = new Object[size]; 456 cont.size= size; 457 System.arraycopy(elementData, 0, cont.elementData, 0, size); 458 ret= cont; 459 } 460 return ret; 461 } 462 synchronized public ListIterator listIterator() 463 { 464 return listIterator(0); 465 } 466 467 /** The iterator keeps a copy of the list. Changes to InterfaceContainer do not 468 * affect the data of the iterator. Conversly, changes to the iterator are effect 469 * InterfaceContainer. 470 */ 471 synchronized public ListIterator listIterator(int index) 472 { 473 if (elementData != null) 474 { 475 InterfaceContainer aCopy= (InterfaceContainer) clone(); 476 return new LstItr(aCopy, index); 477 } 478 return null; 479 } 480 /** 481 * Removes the element at the specified position in this list. 482 * Shifts any subsequent elements to the left (subtracts one from their 483 * indices). 484 * 485 * @param index the index of the element to removed. 486 * @return the element that was removed from the list. 487 * @throws IndexOutOfBoundsException if index out of range <tt>(index 488 * < 0 || index >= size())</tt>. 489 */ 490 synchronized public Object remove(int index) 491 { 492 Object ret= null; 493 if (elementData != null) 494 { 495 RangeCheck(index); 496 ret= elementData[index]; 497 498 int numMoved = size - index - 1; 499 if (numMoved > 0) 500 System.arraycopy(elementData, index+1, elementData, index, 501 numMoved); 502 elementData[--size] = null; // Let gc do its work 503 } 504 return ret; 505 } 506 507 508 /** Parameter obj may */ 509 synchronized public boolean remove(Object obj) 510 { 511 boolean ret= false; 512 if (elementData != null && obj != null) 513 { 514 int index= indexOf(obj); 515 if (index != -1) 516 { 517 ret= true; 518 remove(index); 519 } 520 } 521 return ret; 522 } 523 524 synchronized public boolean removeAll(Collection collection) 525 { 526 boolean retVal= false; 527 if (elementData != null && collection != null) 528 { 529 Iterator it= collection.iterator(); 530 while (it.hasNext()) 531 { 532 Object obj= it.next(); 533 boolean bMod= remove( obj); 534 if (bMod) 535 retVal= true; 536 } 537 } 538 return retVal; 539 } 540 541 synchronized public boolean retainAll(Collection collection) 542 { 543 boolean retVal= false; 544 if (elementData != null && collection != null) 545 { 546 // iterate over data 547 Object[] arRetained= new Object[size]; 548 int indexRetained= 0; 549 for(int i= 0; i < size; i++) 550 { 551 Object curElem= elementData[i]; 552 // try to find the element in collection 553 Iterator itColl= collection.iterator(); 554 boolean bExists= false; 555 while (itColl.hasNext()) 556 { 557 if (curElem == itColl.next()) 558 { 559 // current element is in collection 560 bExists= true; 561 break; 562 } 563 } 564 if (bExists == false) 565 { 566 itColl= collection.iterator(); 567 while (itColl.hasNext()) 568 { 569 Object o= itColl.next(); 570 if (o != null) 571 { 572 if (UnoRuntime.areSame(o, curElem)) 573 { 574 bExists= true; 575 break; 576 } 577 } 578 } 579 } 580 if (bExists == true) 581 arRetained[indexRetained++]= curElem; 582 } 583 retVal= size != indexRetained; 584 if (indexRetained > 0) 585 { 586 elementData= arRetained; 587 size= indexRetained; 588 } 589 } 590 return retVal; 591 } 592 593 594 /** Not supported. 595 * @param index index of element to replace. 596 * @param element element to be stored at the specified position. 597 * @return the element previously at the specified position. 598 * @throws IndexOutOfBoundsException if index out of range 599 * <tt>(index < 0 || index >= size())</tt>. 600 */ 601 synchronized public Object set(int index, Object element) 602 { 603 Object ret= null; 604 if (elementData != null && element != null) 605 { 606 RangeCheck(index); 607 ret = elementData[index]; 608 elementData[index] = element; 609 } 610 return ret; 611 } 612 613 /** 614 * Returns the number of elements in this list. 615 * 616 * @return the number of elements in this list. 617 */ 618 synchronized public int size() 619 { 620 if (elementData != null) 621 return size; 622 return 0; 623 } 624 625 626 /** 627 * Returns an array containing all of the elements in this list 628 * in the correct order. 629 * 630 * @return an array containing all of the elements in this list 631 * in the correct order. 632 */ 633 synchronized public Object[] toArray() 634 { 635 if (elementData != null) 636 { 637 Object[] result = new Object[size]; 638 System.arraycopy(elementData, 0, result, 0, size); 639 return result; 640 } 641 return null; 642 } 643 644 /** 645 * Returns an array containing all of the elements in this list in the 646 * correct order. The runtime type of the returned array is that of the 647 * specified array. If the list fits in the specified array, it is 648 * returned therein. Otherwise, a new array is allocated with the runtime 649 * type of the specified array and the size of this list.<p> 650 * 651 * If the list fits in the specified array with room to spare (i.e., the 652 * array has more elements than the list), the element in the array 653 * immediately following the end of the collection is set to 654 * <tt>null</tt>. This is useful in determining the length of the list 655 * <i>only</i> if the caller knows that the list does not contain any 656 * <tt>null</tt> elements. 657 * 658 * @param a the array into which the elements of the list are to 659 * be stored, if it is big enough; otherwise, a new array of the 660 * same runtime type is allocated for this purpose. 661 * @return an array containing the elements of the list. 662 * @throws ArrayStoreException if the runtime type of a is not a supertype 663 * of the runtime type of every element in this list. 664 */ 665 synchronized public Object[] toArray(Object a[]) 666 { 667 if (a.length < size) 668 a = (Object[])java.lang.reflect.Array.newInstance( 669 a.getClass().getComponentType(), size); 670 if (elementData != null) 671 System.arraycopy(elementData, 0, a, 0, size); 672 673 if (a.length > size) 674 a[size] = null; 675 676 return a; 677 } 678 679 /** 680 * Check if the given index is in range. If not, throw an appropriate 681 * runtime exception. 682 */ 683 private void RangeCheck(int index) 684 { 685 if (index >= size || index < 0) 686 throw new IndexOutOfBoundsException( 687 "Index: "+index+", Size: "+size); 688 } 689 690 public void disposeAndClear(EventObject evt) 691 { 692 Iterator aIt; 693 synchronized (this) 694 { 695 aIt= iterator(); 696 // Container freigeben, falls im disposing neue Eintraege kommen 697 // set the member to null, the iterator delete the values 698 clear(); 699 elementData= null; 700 size= 0; 701 } 702 if (aIt != null) 703 { 704 while( aIt.hasNext() ) 705 { 706 try 707 { 708 Object o= aIt.next(); 709 XEventListener evtListener= UnoRuntime.queryInterface( 710 XEventListener.class, o); 711 if( evtListener != null ) 712 evtListener.disposing( evt ); 713 } 714 catch ( RuntimeException e) 715 { 716 // be robust, if e.g. a remote bridge has disposed already. 717 // there is no way, to delegate the error to the caller :o(. 718 } 719 } 720 } 721 } 722 723 724 private class Itr implements Iterator 725 { 726 InterfaceContainer dataIt; 727 /** 728 * Index of element to be returned by subsequent call to next. 729 */ 730 int cursor= 0; 731 /** 732 * Index of element returned by most recent call to next or 733 * previous. Reset to -1 if this element is deleted by a call 734 * to remove. 735 */ 736 int lastRet = -1; 737 738 /** The object that has been returned by most recent call to next 739 * or previous. Reset to null if this element is deleted by a call 740 * to remove. 741 */ 742 Object lastRetObj= null; 743 744 Itr(InterfaceContainer _data) 745 { 746 dataIt= _data; 747 } 748 749 synchronized public boolean hasNext() 750 { 751 return cursor !=dataIt.size(); 752 } 753 754 public synchronized Object next() 755 { 756 try 757 { 758 Object next = dataIt.get(cursor); 759 lastRet = cursor++; 760 lastRetObj= next; 761 return next; 762 } 763 catch(java.lang.IndexOutOfBoundsException e) 764 { 765 throw new java.util.NoSuchElementException(); 766 } 767 } 768 769 /** Removes the interface from the list, that has been last returned by a 770 * call to next(). This is done according to the specification of the interface 771 * method. The element is also removed from InterfaceContainer but independent 772 * of the location. If the element is multiple times in InterfaceContainer then 773 * it is up to the java.util.ArrayList implementation what element is removed. 774 */ 775 public synchronized void remove() 776 { 777 if (lastRet == -1) 778 throw new IllegalStateException(); 779 // Remove the entry from InterfaceContainer. 780 InterfaceContainer.this.remove(lastRetObj); 781 dataIt.remove(lastRet); 782 783 if (lastRet < cursor) 784 cursor--; 785 lastRet = -1; 786 lastRetObj= null; 787 } 788 } 789 790 private class LstItr extends Itr implements ListIterator 791 { 792 793 LstItr(InterfaceContainer _data, int _index) 794 { 795 super(_data); 796 cursor= _index; 797 } 798 799 /** Inserts an element to the iterators list according to the specification 800 * of this interface method. The element is also added to InterfaceContainer 801 * but its location within the list cannot be guaranteed. 802 */ 803 public synchronized void add(Object o) 804 { 805 InterfaceContainer.this.add(o); 806 dataIt.add(cursor++, o); 807 lastRet = -1; 808 lastRetObj= null; 809 } 810 811 synchronized public boolean hasPrevious() 812 { 813 return cursor != 0; 814 } 815 816 synchronized public int nextIndex() 817 { 818 return cursor; 819 } 820 821 public synchronized Object previous() 822 { 823 try 824 { 825 Object previous = dataIt.get(--cursor); 826 lastRet = cursor; 827 lastRetObj= previous; 828 return previous; 829 } catch(IndexOutOfBoundsException e) 830 { 831 throw new NoSuchElementException(); 832 } 833 } 834 835 synchronized public int previousIndex() 836 { 837 return cursor-1; 838 } 839 840 /** This is not possible since several iterators can modify InterfaceContainer 841 */ 842 public synchronized void set(Object o) 843 { 844 throw new UnsupportedOperationException(); 845 } 846 847 848 } // class LstItr 849 } 850 851