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