blob: e6f7e2e148d5f3f7e493b6522e48ab1ba2e9bd2d [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package com.sun.star.lib.uno.helper;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
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 copys 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
* <pre>
* 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.
* </pre>
*
* 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, allthough
* 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.
* Unfortunatly, 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, allthough they might work well in the "main list" (as opposed to sub list).
*/
public class InterfaceContainer implements Cloneable
{
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;
//private ArrayList data= new ArrayList();
/** 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 <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> 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 <tt>ArrayList</tt> 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 <tt>true</tt> (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
* <tt>(index &lt; 0 || index &gt; size())</tt>.
*/
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 <tt>(index
* &lt; 0 || index &gt; size())</tt>.
*/
synchronized public boolean addAll(Collection c)
{
int numNew = c.size();
ensureCapacity(size + numNew);
Iterator e = c.iterator();
for (int i=0; i<numNew; i++)
{
Object o= e.next();
if (o != null)
elementData[size++] = o;
}
return numNew != 0;
}
/**
* Inserts all of the elements in the specified Collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified Collection's iterator.
*
* @param index index at which to insert first element
* from the specified collection.
* @param c elements to be inserted into this list.
* @throws IndexOutOfBoundsException if index out of range <tt>(index
* &lt; 0 || index &gt; size())</tt>.
*/
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; i<numNew; i++)
{
elementData[index++]= arColl[i];
}
size += numNew;
ret= numNew != 0;
}
return ret;
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
synchronized public void clear()
{
if (elementData != null)
{
// Let gc do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
}
/**
* Returns <tt>true</tt> 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 (false == 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 <tt>(index
* &lt; 0 || index &gt;= size())</tt>.
*/
synchronized public Object get(int index)
{
if (elementData != null)
{
RangeCheck(index);
return elementData[index];
}
return null;
}
/**
* Searches for the first occurence of the given argument, testing
* for equality using the <tt>equals</tt> method.
*
* @param elem an object.
* @return the index of the first occurrence of the argument in this
* list; returns <tt>-1</tt> if the object is not found.
* @see Object#equals(Object)
*/
synchronized public int indexOf(Object elem)
{
int index= -1;
if (elementData != null && elem != null)
{
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 <tt>true</tt> if this list has no elements;
* <tt>false</tt> 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)
{
int index= -1;
if (elementData != null && elem != null)
{
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 <tt>ArrayList</tt> instance. The contained
* references are copied but the objects not.
*
* @return a clone of this <tt>List</tt> instance.
*/
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. Conversly, 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 <tt>(index
* &lt; 0 || index &gt;= size())</tt>.
*/
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)
{
boolean retVal= false;
if (elementData != null && collection != null)
{
// 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 == false)
{
itColl= collection.iterator();
while (itColl.hasNext())
{
Object o= itColl.next();
if (o != null)
{
if (UnoRuntime.areSame(o, curElem))
{
bExists= true;
break;
}
}
}
}
if (bExists == true)
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
* <tt>(index &lt; 0 || index &gt;= size())</tt>.
*/
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.<p>
*
* 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
* <tt>null</tt>. This is useful in determining the length of the list
* <i>only</i> if the caller knows that the list does not contain any
* <tt>null</tt> 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)
{
throw new java.util.NoSuchElementException();
}
}
/** 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)
{
throw new NoSuchElementException();
}
}
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
}