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