/* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ package com.sun.star.lib.uno.helper; import java.util.Iterator; import java.util.ListIterator; import java.util.Collection; import com.sun.star.lang.EventObject; import com.sun.star.lang.XEventListener; import com.sun.star.uno.UnoRuntime; /** * This class is a container for interfaces. * * It is intended to be used as storage for UNO interface of a specific type. * The client has to ensure that the container contains only elements of the same * type. If one needs to store different types, then one uses OMultiTypeInterfaceContainer. * When the client calls disposeAndClear, the contained objects are queried for * com.sun.star.lang.XEventListener and disposing is called. Afterwards * the list cannot be used anymore. * * This list does not allow null values. * All methods are thread-safe. The same holds true for * iterators, issued by this class. Several iterators can exist at the same time and can also * be modified (java.util.ListIterator.add, java.util.ListIterator.remove etc.). To make this work, * the InterfaceContainer provides the iterators with copies of the list's data. * The add and remove calls on the iterator modify the data in the iterator's list as well as * in InterfaceContainer. Modification on InterfaceContainer, however, are not * synchronized with existing iterators. For example *
 * InterfaceContainer cont= new InterfaceContainer();
 * ListIterator it= cont.listIterator();
 *
 * cont.add( someInterface);
 * // one cannot obtain someInterface through iterator it,
 * // instead get a new iterator
 * it= cont.listIterator();
 * // it now keeps a fresh copy of cont and hence contains someInterface
 *
 * // Adding an interface on the iterator will cause the interface also to be added
 * // to InterfaceContainer
 * it.add( someOtherInterface);
 * // someOtherInterface is now in it and cont
 * ListIterator it2= cont.listIterator();
 * //someOtherInterface can also be obtained by all newly created iterators, e.g. it2.
 * 
* * The add and remove methods of an iterator work on a particular location within a list, * dependent on what the value of the iterator's cursor is. After the call the value at the * appropriate position has been modified. Since the iterator received a copy of InterfaceContainer's * data, InterfaceContainer may have been modified (by List methods or through other iterators). * Therefore both data sets may not contain the same elements anymore. Consequently, a List method * that modifies data, does not modify InterfaceContainer's data at a certain index * (according to the iterators cursor). Instead, new elements are added at the end of list. When * Iterator.remove is called, then the first occurrence of that element in InterfaceContainer * is removed. * ListIterator.set is not supported. * * A lot of methods resemble those of the to java.util.List interface, although * this class does not implement it. However, the list iterators returned, for example by * the listIterator method implement the java.util.ListIterator interface. * Implementing the List interface would mean to support all none - optional methods as * prescribed by the interface declaration. Among those is the subList method which returns * a range of values of the list's data wrapped in a List implementation. Changes to the sub * list have to cause changes in the main list. This is a problem, since this class is to be * used in a multi-threaded environment. The sub list could work on a copy as the iterators * do, but all the functions which work on an given index could not be properly supported. * Unfortunately, the List interface documentation states that all optional methods implemented * by the list have to be implemented in the sub list. That would mean to do without all those * critical methods, although they might work well in the "main list" (as opposed to sub list). */ public class InterfaceContainer implements Cloneable { static final boolean DEBUG= false; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ Object elementData[]; /** * The size of the ArrayList (the number of elements it contains). */ private int size; /** Creates a new instance of InterfaceContainer */ public InterfaceContainer() { this(10); } /** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list. * @exception IllegalArgumentException if the specified initial capacity * is negative */ public InterfaceContainer(int initialCapacity) { if (initialCapacity < 0) throw new java.lang.IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } /** * Trims the capacity of this ArrayList instance to be the * list's current size. An application can use this operation to minimize * the storage of an ArrayList instance. */ synchronized public void trimToSize() { int oldCapacity = elementData.length; if (size < oldCapacity) { Object oldData[] = elementData; elementData = new Object[size]; System.arraycopy(oldData, 0, elementData, 0, size); } } /** * Increases the capacity of this ArrayList instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity. */ synchronized public void ensureCapacity(int minCapacity) { int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { Object oldData[] = elementData; int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; elementData = new Object[newCapacity]; System.arraycopy(oldData, 0, elementData, 0, size); } } /** * Appends the specified element to the end of this list. * * @param o element to be appended to this list. * @return true (as per the general contract of Collection.add). */ synchronized public boolean add(Object o) { boolean ret= false; if (elementData != null && o != null) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = o; ret= true; } return ret; } /** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted. * @param element element to be inserted. * @throws IndexOutOfBoundsException if index is out of range * (index < 0 || index > size()). */ synchronized public void add(int index, Object element) { if (elementData != null && element != null) { if (index > size || index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); ensureCapacity(size+1); System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } } /** * Appends all of the elements in the specified Collection to the end of * this list, in the order that they are returned by the * specified Collection's Iterator. The behavior of this operation is * undefined if the specified Collection is modified while the operation * is in progress. (This implies that the behavior of this call is * undefined if the specified Collection is this list, and this * list is nonempty.) * * @param c the elements to be inserted into this list. * @throws IndexOutOfBoundsException if index out of range (index * < 0 || index > size()). */ synchronized public boolean addAll(Collection c) { int numNew = c.size(); ensureCapacity(size + numNew); Iterator e = c.iterator(); for (int i=0; i(index * < 0 || index > size()). */ synchronized public boolean addAll(int index, Collection c) { boolean ret= false; if (elementData != null) { if (index > size || index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); // only add the non-null elements int sizeCol= c.size(); Object[] arColl= new Object[sizeCol]; Iterator icol= c.iterator(); int curIndex= 0; for (int i=0; i < sizeCol; i++) { Object o= icol.next(); if (o != null) arColl[curIndex++]= o; } int numNew = curIndex; ensureCapacity(size + numNew); // Increments modCount!! int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); for (int i=0; itrue if this list contains the specified element. * * @param elem element whose presence in this List is to be tested. */ synchronized public boolean contains(Object elem) { return indexOf(elem) >= 0; } synchronized public boolean containsAll(Collection collection) { boolean retVal= true; if (elementData != null && collection != null) { Iterator it= collection.iterator(); while (it.hasNext()) { Object obj= it.next(); if (!contains(obj)) { retVal= false; break; } } } return retVal; } /** * Returns the element at the specified position in this list. * * @param index index of element to return. * @return the element at the specified position in this list. * @throws IndexOutOfBoundsException if index is out of range (index * < 0 || index >= size()). */ synchronized public Object get(int index) { if (elementData != null) { RangeCheck(index); return elementData[index]; } return null; } /** * Searches for the first occurrence of the given argument, testing * for equality using the equals method. * * @param elem an object. * @return the index of the first occurrence of the argument in this * list; returns -1 if the object is not found. * @see Object#equals(Object) */ synchronized public int indexOf(Object elem) { if (elementData == null || elem == null) { return -1; } int index= -1; for (int i = 0; i < size; i++) { if (elem == elementData[i]) { index= i; break; } } if (index == -1) { for (int i = 0; i < size; i++) { if (UnoRuntime.areSame(elem, elementData[i])) { index= i; break; } } } return index; } /** * Tests if this list has no elements. * * @return true if this list has no elements; * false otherwise. */ synchronized public boolean isEmpty() { return size == 0; } synchronized public Iterator iterator() { if (elementData != null) { InterfaceContainer aCopy= (InterfaceContainer) clone(); return new Itr(aCopy); } return null; } /** * Returns the index of the last occurrence of the specified object in * this list. * * @param elem the desired element. * @return the index of the last occurrence of the specified object in * this list; returns -1 if the object is not found. */ synchronized public int lastIndexOf(Object elem) { if (elementData == null || elem == null) { return -1; } int index= -1; for (int i = size-1; i >= 0; i--) { if (elem == elementData[i]) { index= i; break; } } if (index == -1) { for (int i = size-1; i >= 0; i--) { if (UnoRuntime.areSame(elem, elementData[i])) { index= i; break; } } } return index; } /** * Returns a shallow copy of this ArrayList instance. The contained * references are copied but the objects not. * * @return a clone of this List instance. */ @Override synchronized public Object clone() { Object ret= null; if (elementData != null) { InterfaceContainer cont= new InterfaceContainer(); cont.elementData = new Object[size]; cont.size= size; System.arraycopy(elementData, 0, cont.elementData, 0, size); ret= cont; } return ret; } synchronized public ListIterator listIterator() { return listIterator(0); } /** The iterator keeps a copy of the list. Changes to InterfaceContainer do not * affect the data of the iterator. Conversely, changes to the iterator are effect * InterfaceContainer. */ synchronized public ListIterator listIterator(int index) { if (elementData != null) { InterfaceContainer aCopy= (InterfaceContainer) clone(); return new LstItr(aCopy, index); } return null; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to removed. * @return the element that was removed from the list. * @throws IndexOutOfBoundsException if index out of range (index * < 0 || index >= size()). */ synchronized public Object remove(int index) { Object ret= null; if (elementData != null) { RangeCheck(index); ret= elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work } return ret; } /** Parameter obj may */ synchronized public boolean remove(Object obj) { boolean ret= false; if (elementData != null && obj != null) { int index= indexOf(obj); if (index != -1) { ret= true; remove(index); } } return ret; } synchronized public boolean removeAll(Collection collection) { boolean retVal= false; if (elementData != null && collection != null) { Iterator it= collection.iterator(); while (it.hasNext()) { Object obj= it.next(); boolean bMod= remove( obj); if (bMod) retVal= true; } } return retVal; } synchronized public boolean retainAll(Collection collection) { if (elementData == null || collection == null) { return false; } boolean retVal= false; // iterate over data Object[] arRetained= new Object[size]; int indexRetained= 0; for(int i= 0; i < size; i++) { Object curElem= elementData[i]; // try to find the element in collection Iterator itColl= collection.iterator(); boolean bExists= false; while (itColl.hasNext()) { if (curElem == itColl.next()) { // current element is in collection bExists= true; break; } } if (!bExists) { itColl= collection.iterator(); while (itColl.hasNext()) { Object o= itColl.next(); if (o != null) { if (UnoRuntime.areSame(o, curElem)) { bExists= true; break; } } } } if (bExists) arRetained[indexRetained++]= curElem; } retVal= size != indexRetained; if (indexRetained > 0) { elementData= arRetained; size= indexRetained; } return retVal; } /** Not supported. * @param index index of element to replace. * @param element element to be stored at the specified position. * @return the element previously at the specified position. * @throws IndexOutOfBoundsException if index out of range * (index < 0 || index >= size()). */ synchronized public Object set(int index, Object element) { Object ret= null; if (elementData != null && element != null) { RangeCheck(index); ret = elementData[index]; elementData[index] = element; } return ret; } /** * Returns the number of elements in this list. * * @return the number of elements in this list. */ synchronized public int size() { if (elementData != null) return size; return 0; } /** * Returns an array containing all of the elements in this list * in the correct order. * * @return an array containing all of the elements in this list * in the correct order. */ synchronized public Object[] toArray() { if (elementData != null) { Object[] result = new Object[size]; System.arraycopy(elementData, 0, result, 0, size); return result; } return null; } /** * Returns an array containing all of the elements in this list in the * correct order. The runtime type of the returned array is that of the * specified array. If the list fits in the specified array, it is * returned therein. Otherwise, a new array is allocated with the runtime * type of the specified array and the size of this list.

* * If the list fits in the specified array with room to spare (i.e., the * array has more elements than the list), the element in the array * immediately following the end of the collection is set to * null. This is useful in determining the length of the list * only if the caller knows that the list does not contain any * null elements. * * @param a the array into which the elements of the list are to * be stored, if it is big enough; otherwise, a new array of the * same runtime type is allocated for this purpose. * @return an array containing the elements of the list. * @throws ArrayStoreException if the runtime type of a is not a supertype * of the runtime type of every element in this list. */ synchronized public Object[] toArray(Object a[]) { if (a.length < size) a = (Object[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); if (elementData != null) System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } /** * Check if the given index is in range. If not, throw an appropriate * runtime exception. */ private void RangeCheck(int index) { if (index >= size || index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); } public void disposeAndClear(EventObject evt) { Iterator aIt; synchronized (this) { aIt= iterator(); // Container freigeben, falls im disposing neue Eintraege kommen // set the member to null, the iterator delete the values clear(); elementData= null; size= 0; } if (aIt != null) { while( aIt.hasNext() ) { try { Object o= aIt.next(); XEventListener evtListener= UnoRuntime.queryInterface( XEventListener.class, o); if( evtListener != null ) evtListener.disposing( evt ); } catch ( RuntimeException e) { // be robust, if e.g. a remote bridge has disposed already. // there is no way, to delegate the error to the caller :o(. } } } } private class Itr implements Iterator { InterfaceContainer dataIt; /** * Index of element to be returned by subsequent call to next. */ int cursor= 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; /** The object that has been returned by most recent call to next * or previous. Reset to null if this element is deleted by a call * to remove. */ Object lastRetObj= null; Itr(InterfaceContainer _data) { dataIt= _data; } synchronized public boolean hasNext() { return cursor !=dataIt.size(); } public synchronized Object next() { try { Object next = dataIt.get(cursor); lastRet = cursor++; lastRetObj= next; return next; } catch(java.lang.IndexOutOfBoundsException e) { java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException(); ex2.initCause(e); throw ex2; } } /** Removes the interface from the list, that has been last returned by a * call to next(). This is done according to the specification of the interface * method. The element is also removed from InterfaceContainer but independent * of the location. If the element is multiple times in InterfaceContainer then * it is up to the java.util.ArrayList implementation what element is removed. */ public synchronized void remove() { if (lastRet == -1) throw new IllegalStateException(); // Remove the entry from InterfaceContainer. InterfaceContainer.this.remove(lastRetObj); dataIt.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; lastRetObj= null; } } private class LstItr extends Itr implements ListIterator { LstItr(InterfaceContainer _data, int _index) { super(_data); cursor= _index; } /** Inserts an element to the iterators list according to the specification * of this interface method. The element is also added to InterfaceContainer * but its location within the list cannot be guaranteed. */ public synchronized void add(Object o) { InterfaceContainer.this.add(o); dataIt.add(cursor++, o); lastRet = -1; lastRetObj= null; } synchronized public boolean hasPrevious() { return cursor != 0; } synchronized public int nextIndex() { return cursor; } public synchronized Object previous() { try { Object previous = dataIt.get(--cursor); lastRet = cursor; lastRetObj= previous; return previous; } catch(IndexOutOfBoundsException e) { java.util.NoSuchElementException ex2 = new java.util.NoSuchElementException(); ex2.initCause(e); throw ex2; } } synchronized public int previousIndex() { return cursor-1; } /** This is not possible since several iterators can modify InterfaceContainer */ public synchronized void set(Object o) { throw new UnsupportedOperationException(); } } // class LstItr }