diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ridljar/com/sun/star/lib/uno/helper | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ridljar/com/sun/star/lib/uno/helper')
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/ComponentBase.java | 136 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/Factory.java | 291 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java | 864 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java | 155 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/PropertySet.java | 1103 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java | 1111 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/UnoUrl.java | 401 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java | 94 | ||||
-rw-r--r-- | ridljar/com/sun/star/lib/uno/helper/WeakBase.java | 101 |
9 files changed, 4256 insertions, 0 deletions
diff --git a/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java new file mode 100644 index 000000000..d886ef702 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/ComponentBase.java @@ -0,0 +1,136 @@ +/* + * 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 com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.Type; + +/** This class can be used as the base class for UNO components. In addition to the functionality ,which + * is inherited from WeakBase, it implements com.sun.star.lang.XComponent. + */ +public class ComponentBase extends WeakBase implements XComponent +{ + private static final boolean DEBUG= false; + protected MultiTypeInterfaceContainer listenerContainer; + protected boolean bInDispose= false; + protected boolean bDisposed= false; + static final Type EVT_LISTENER_TYPE= new Type(XEventListener.class); + + + /** Creates a new instance of CompBase */ + public ComponentBase() + { + super(); + listenerContainer= new MultiTypeInterfaceContainer(); + } + + /** Override to perform extra clean-up work. Provided for subclasses. It is + called during dispose() + */ + protected void preDisposing() + { + } + /** Override to become notified right before the disposing action is performed. + */ + protected void postDisposing() + { + } + + + /** Method of XComponent. It is called by the owning client when the component is not needed + * anymore. The registered listeners are notified that this method has been called. + */ + public void dispose() + { + // Determine in a thread-safe way if this is the first call to this method. + // Only then we proceed with the notification of event listeners. + // It is an error to call this method more than once. + boolean bDoDispose= false; + synchronized (this) + { + if ( ! bInDispose && ! bDisposed) + { + bDoDispose= true; + bInDispose= true; + } + } + // The notification occurs in an unsynchronized block in order to avoid + // deadlocks if one of the listeners calls back in a different thread on + // a synchronized method which uses the same object. + if (bDoDispose) + { + try + { + preDisposing(); + listenerContainer.disposeAndClear(new EventObject(this)); + //notify subclasses that disposing is in progress + postDisposing(); + } + finally + { + // finally makes sure that the flags are set even if a RuntimeException is thrown. + // That ensures that this function is only called once. + synchronized (this) + { + bDisposed= true; + bInDispose= false; + } + } + } + else + { + // in a multithreaded environment, it can't be avoided, that dispose is called twice. + // However this condition is traced, because it MAY indicate an error. + if (DEBUG) + System.out.println("OComponentHelper::dispose() - dispose called twice" ); + } + } + + /** Method of XComponent. + */ + public void removeEventListener(XEventListener xEventListener) + { + listenerContainer.removeInterface( EVT_LISTENER_TYPE, xEventListener); + } + + public void addEventListener(XEventListener listener) + { + boolean bDoDispose= false; + synchronized (this) + { + if (bDisposed || bInDispose) + bDoDispose= true; + else + listenerContainer.addInterface(EVT_LISTENER_TYPE, listener); + } + if (bDoDispose ) + { + listener.disposing( new EventObject(this)); + } + } + + @Override + protected void finalize() throws Throwable + { + if ( ! bInDispose && ! bDisposed) + dispose(); + super.finalize(); + } +} diff --git a/ridljar/com/sun/star/lib/uno/helper/Factory.java b/ridljar/com/sun/star/lib/uno/helper/Factory.java new file mode 100644 index 000000000..056d9549d --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/Factory.java @@ -0,0 +1,291 @@ +/* + * 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 com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XSingleComponentFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XInitialization; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.uno.UnoRuntime; + + +/** Factory helper class supporting com.sun.star.lang.XServiceInfo and + com.sun.star.lang.XSingleComponentFactory. + + <p> + Note: This factory implementation does not support lang.XSingleServiceFactory. + </p> +*/ +public class Factory + extends ComponentBase + implements XSingleComponentFactory, XServiceInfo +{ + private static final boolean DEBUG = false; + + /** Creates an object factory supporting interfaces + com.sun.star.lang.XSingleComponentFactory and + com.sun.star.lang.XServiceInfo + + @param impl_class + implementation class + @param impl_name + implementation name + @param supported_services + services implemented + @return + object factory + + @since UDK 3.2.13 + */ + public static XSingleComponentFactory createComponentFactory( + Class impl_class, String impl_name, String supported_services [] ) + throws com.sun.star.uno.RuntimeException + { + return new Factory( impl_class, impl_name, supported_services ); + } + + /** Creates an object factory supporting interfaces + com.sun.star.lang.XSingleComponentFactory and + com.sun.star.lang.XServiceInfo + + The implementation name is the name of the implementation class. + + @param impl_class + implementation class + @param supported_services + services implemented + @return + object factory + */ + public static XSingleComponentFactory createComponentFactory( + Class impl_class, String supported_services [] ) + throws com.sun.star.uno.RuntimeException + { + return createComponentFactory( + impl_class, impl_class.getName(), supported_services ); + } + /** Writes component's implementation info to given registry key. + + @param impl_name + name of implementation + @param supported_services + supported services of implementation + @param xKey + registry key to write to + @return + success + */ + public static boolean writeRegistryServiceInfo( + String impl_name, String supported_services [], XRegistryKey xKey ) + { + try + { + XRegistryKey xNewKey = xKey.createKey( "/" + impl_name + "/UNO/SERVICES" ); + for ( int nPos = 0; nPos < supported_services.length; ++nPos ) + { + xNewKey.createKey( supported_services[ nPos ] ); + } + return true; + } + catch (com.sun.star.registry.InvalidRegistryException exc) + { + if (DEBUG) + { + System.err.println( + "##### " + Factory.class.getName() + ".writeRegistryServiceInfo -- exc: " + + exc.toString() ); + } + } + return false; + } + + + private final String m_impl_name; + private final String [] m_supported_services; + private final Class<?> m_impl_class; + private final java.lang.reflect.Method m_method; + private final java.lang.reflect.Constructor m_ctor; + + private Factory( + Class impl_class, String impl_name, String supported_services [] ) + throws com.sun.star.uno.DeploymentException + { + m_impl_name = impl_name; + m_supported_services = supported_services; + m_impl_class = impl_class; + + Class params [] = new Class [] { XComponentContext.class }; + + if (!java.lang.reflect.Modifier.isPublic( impl_class.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("class " + impl_class + " is not public"); + } + + java.lang.reflect.Method tmpMethod = null; + try + { + // seeking for "public static Object __create( XComponentContext )" + tmpMethod = m_impl_class.getMethod( "__create", params ); + int mod = tmpMethod.getModifiers(); + if (!tmpMethod.getReturnType().equals( Object.class ) || + !java.lang.reflect.Modifier.isStatic( mod ) || + !java.lang.reflect.Modifier.isPublic( mod )) + { + tmpMethod = null; + } + } + catch (Exception exc) + { + } + m_method = tmpMethod; + + + java.lang.reflect.Constructor tmpCtor = null; + if (null == m_method) + { + try + { + // ctor with context + tmpCtor = m_impl_class.getConstructor( params ); + } + catch (Exception exc) + { + } + if (tmpCtor != null) + { + if (!java.lang.reflect.Modifier.isPublic( tmpCtor.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("constructor with XComponentContext param for class " + impl_class + " is not public"); + } + } + else + { + // else take default ctor + java.lang.reflect.Constructor defaultCtor; + try + { + defaultCtor = m_impl_class.getConstructor(new Class[0]); + } + catch (Exception exc2) + { + throw new com.sun.star.uno.DeploymentException(exc2, "class " + impl_class + " has no means of creating it, cannot find a __create method or a useful constructor."); + } + if (!java.lang.reflect.Modifier.isPublic( defaultCtor.getModifiers() )) + { + throw new com.sun.star.uno.DeploymentException("default constructor for class " + impl_class + " is not public"); + } + } + } + m_ctor = tmpCtor; + } + + + private Object instantiate( XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + try + { + if (DEBUG) + System.out.print( "instantiating " + m_impl_class.toString() + " using " ); + if (null != m_method) + { + if (DEBUG) + System.out.println( "__create( XComponentContext )..." ); + return m_method.invoke( null, new Object [] { xContext } ); + } + if (null != m_ctor) + { + if (DEBUG) + System.out.println( "ctor( XComponentContext )..." ); + return m_ctor.newInstance( new Object [] { xContext } ); + } + if (DEBUG) + System.out.println( "default ctor..." ); + return m_impl_class.newInstance(); // default ctor + } + catch (java.lang.reflect.InvocationTargetException exc) + { + Throwable targetException = exc.getTargetException(); + if (targetException instanceof java.lang.RuntimeException) + throw (java.lang.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.RuntimeException) + throw (com.sun.star.uno.RuntimeException)targetException; + else if (targetException instanceof com.sun.star.uno.Exception) + throw (com.sun.star.uno.Exception)targetException; + else + throw new com.sun.star.uno.Exception(targetException, targetException.getMessage(), this); + } + catch (IllegalAccessException exc) + { + throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this); + } + catch (InstantiationException exc) + { + throw new com.sun.star.uno.RuntimeException( exc, exc.getMessage(), this); + } + } + // XSingleComponentFactory impl + + public final Object createInstanceWithContext( + XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + return instantiate( xContext ); + } + + public final Object createInstanceWithArgumentsAndContext( + Object arguments [], XComponentContext xContext ) + throws com.sun.star.uno.Exception + { + Object inst = instantiate( xContext ); + XInitialization xInit = UnoRuntime.queryInterface( + XInitialization.class, inst ); + if (null == xInit) + { + throw new com.sun.star.lang.IllegalArgumentException( + "cannot pass arguments to component; no XInitialization implemented!", this, + (short)0 ); + } + xInit.initialize( arguments ); + return inst; + } + + // XServiceInfo impl + + public final String getImplementationName() + { + return m_impl_name; + } + + public final boolean supportsService( String service_name ) + { + for ( int nPos = 0; nPos < m_supported_services.length; ++nPos ) + { + if (m_supported_services[ nPos ].equals( service_name )) + return true; + } + return false; + } + + public final String [] getSupportedServiceNames() + { + return m_supported_services; + } +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java new file mode 100644 index 000000000..e858ced81 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/InterfaceContainer.java @@ -0,0 +1,864 @@ +/* + * 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 + * <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, 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 a 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]; + } + + private InterfaceContainer(Object[] data) { + elementData = data; + size = elementData == null ? 0 : elementData.length; + } + + /** + * Trims the capacity of this <code>ArrayList</code> instance to be the + * list's current size. An application can use this operation to minimize + * the storage of an <code>ArrayList</code> 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 <code>ArrayList</code> 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 <code>true</code> (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 + * <code>(index < 0 || index > size())</code>. + */ + 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 <code>(index + * < 0 || index > size())</code>. + * @return true if an element was inserted. + */ + 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 <code>(index + * < 0 || index > size())</code>. + * @return true if an element was inserted. + */ + 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 <code>true</code> if this list contains the specified element. + * + * @param elem element whose presence in this List is to be tested. + * @return <code>true</code> if this list contains the specified element. + */ + 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 <code>(index + * < 0 || index >= size())</code>. + */ + 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 <code>equals</code> method. + * + * @param elem an object. + * @return the index of the first occurrence of the argument in this + * list; returns <code>-1</code> 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 <code>true</code> if this list has no elements; + * <code>false</code> 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 <code>ArrayList</code> instance. The contained + * references are copied but the objects not. + * + * @return a clone of this <code>List</code> instance. + */ + @Override + synchronized public Object clone() + { + Object[] data; + if (elementData == null) { + data = null; + } else { + data = new Object[size]; + System.arraycopy(elementData, 0, data, 0, size); + } + return new InterfaceContainer(data); + } + 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. + * @param index the starting offset into the list. + * @return a new iterator. + */ + 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 <code>(index + * < 0 || index >= size())</code>. + */ + 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... or may not. What did the original author want + * to tell us here? + * @param obj the object to be removed. + * @return true if obj was successfully removed from the list. + */ + 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 && 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 + * <code>(index < 0 || index >= size())</code>. + */ + 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 + * <code>null</code>. This is useful in determining the length of the list + * <i>only</i> if the caller knows that the list does not contain any + * <code>null</code> 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(); + // Release containers if new entries occur in disposing; + // 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 +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java new file mode 100644 index 000000000..9b061f81c --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/MultiTypeInterfaceContainer.java @@ -0,0 +1,155 @@ +/* + * 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 com.sun.star.uno.Type; +import com.sun.star.lang.EventObject; +import java.util.HashMap; +import java.util.Map; +import java.util.Iterator; + +public class MultiTypeInterfaceContainer +{ + + private final Map<Object,InterfaceContainer> map= new HashMap<Object,InterfaceContainer>(); + + /** only returns types which have at least one value in InterfaceContainer + * return value can contain an element null, if someone called + * addInterface (null, interf) + * @return an array of types in this container. + */ + synchronized public Type[] getContainedTypes() + { + int size; + Type[] retVal= null; + + if ( (size=map.size()) > 0) + { + Type [] arTypes= new Type[size]; + + int countTypes= 0; + for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet()) + { + InterfaceContainer cont= entry.getValue(); + if (cont != null && cont.size() > 0) + { + Object key = entry.getKey(); + if (key == null) + arTypes[countTypes++]= new Type(); + else if (key instanceof Type) + arTypes[countTypes++]= (Type) key; + else if (key instanceof Class) + arTypes[countTypes++]= new Type((Class) key); + else + arTypes[countTypes++]= new Type(key.getClass()); + } + } + + if (countTypes != size) + { + retVal= new Type[countTypes]; + System.arraycopy(arTypes, 0, retVal, 0, countTypes); + } + else + retVal= arTypes; + } + if (retVal == null) + retVal= new Type[0]; + return retVal; + } + + /** param key can be null + * @param key the object for which the container should be retrieved. + * @return the container that contains the object key, if any. + */ + synchronized public InterfaceContainer getContainer(Object key) + { + InterfaceContainer retVal= null; + for (Map.Entry<Object,InterfaceContainer> entry : map.entrySet()) + { + Object obj= entry.getKey(); + if (obj == null && key == null) + { + retVal= map.get(null); + break; + } + else if( obj != null && obj.equals(key)) + { + retVal= entry.getValue(); + break; + } + } + return retVal; + } + + + synchronized public int addInterface(Object ckey, Object iface) + { + //If the key is a Type then it does not matter if the objects are different + // if they represent the same type. This is because Types overrides hashCode and + // equals. For example: + // Type a= new Type(XInterface.class); + // Type b= new Type(XInterface.class); + // Although a != b , the map interprets both as being the same. + InterfaceContainer cont= map.get(ckey); + if (cont != null) + { + cont.add(iface); + } + else + { + cont= new InterfaceContainer(); + cont.add(iface); + map.put(ckey, cont); + } + return cont.size(); + } + + + synchronized public int removeInterface(Object key, Object iface) + { + int retVal= 0; + InterfaceContainer cont= map.get(key); + if (cont != null) + { + cont.remove(iface); + retVal= cont.size(); + } + return retVal; + } + + public void disposeAndClear(EventObject evt) + { + Iterator<InterfaceContainer> it= null; + synchronized(this) + { + it= map.values().iterator(); + } + while (it.hasNext() ) { + it.next().disposeAndClear(evt); + } + } + + synchronized public void clear() + { + Iterator<InterfaceContainer> it= map.values().iterator(); + while (it.hasNext()) { + it.next().clear(); + } + } +} diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySet.java b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java new file mode 100644 index 000000000..67a4f0c98 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/PropertySet.java @@ -0,0 +1,1103 @@ +/* + * 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 com.sun.star.uno.Type; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.XInterface; +import com.sun.star.uno.Any; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XVetoableChangeListener; +import com.sun.star.beans.PropertyChangeEvent; +import com.sun.star.beans.XPropertySet; +import com.sun.star.beans.Property; +import com.sun.star.beans.PropertyAttribute; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertiesChangeListener; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XFastPropertySet; +import com.sun.star.beans.PropertyVetoException; +import com.sun.star.beans.XMultiPropertySet; +import java.util.Iterator; +import java.util.Collection; +import java.util.HashMap; +import java.lang.reflect.Field; +import com.sun.star.lang.DisposedException; + + +/** This class is an implementation of the interfaces com.sun.star.beans.XPropertySet, + * com.sun.star.beans.XFastPropertySet and com.sun.star.beans.XMultiPropertySet. This + * class has to be inherited to be used. The values of properties are stored in member + * variables of the inheriting class. By overriding the methods + * {@link #convertPropertyValue convertPropertyValue}, + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and + * {@link #getPropertyValue(Property)} one can determine how + * property values are stored. + * When using the supplied implementations of this class then the member variables which + * hold property values have to be declared in the class which inherits last in the inheriting + * chain and they have to be public<p> + * Properties have to be registered by one of the registerProperty methods. They take among other + * arguments an Object named <em>id</em> which has to be a String that represents the name of + * the member variable. The registering has to occur in the constructor of the inheriting class. + * It is no allowed to add or change properties later on.<p> + * Example: + * <pre> + * public class Foo extends PropertySet + * { + * protected int intProp; + * + * public Foo() + * { + * registerProperty("PropertyA", 0, new Type(int.class), (short)0, "intProp"); + * } + * } + * + * </pre> + */ +public class PropertySet extends ComponentBase implements XPropertySet, XFastPropertySet, +XMultiPropertySet +{ + private HashMap<String,Property> _nameToPropertyMap; + private HashMap<Integer,Property> _handleToPropertyMap; + private HashMap<Property,Object> _propertyToIdMap; + private Property[] arProperties; + + private int lastHandle= 1; + + protected XPropertySetInfo propertySetInfo; + protected MultiTypeInterfaceContainer aBoundLC= new MultiTypeInterfaceContainer(); + protected MultiTypeInterfaceContainer aVetoableLC= new MultiTypeInterfaceContainer(); + public PropertySet() + { + super(); + initMappings(); + } + + /** Registers a property with this helper class and associates the argument <em>id</em> with it. + * <em>id</em> is used to identify the storage of the property value. How property values are stored + * and retrieved is determined by the methods {@link #convertPropertyValue convertPropertyValue}, + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and {@link #getPropertyValue(Property) getPropertyValue} + * These methods expect <em>id</em> to be a String which represents the name of a member variable + * which holds the property value. + * Only properties which are registered can be accessed. Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor). + * @param prop The property to be registered. + * @param id Identifies the properties storage. + * @see #getPropertyId + */ + protected void registerProperty(Property prop, Object id) + { + putProperty(prop); + assignPropertyId(prop, id); + } + + /** Registers a property with this helper class and associates the argument id with it. + * It does the same as {@link #registerProperty(Property, Object)}. The first four + * arguments are used to construct a Property object. + * Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor) + * @param name The property's name (Property.Name). + * @param handle The property's handle (Property.Handle). + * @param type The property's type (Property.Type). + * @param attributes The property's attributes (Property.Attributes). + * @param id Identifies the property's storage. + */ + protected void registerProperty(String name, int handle, Type type, short attributes, Object id) + { + Property p= new Property(name, handle, type, attributes); + registerProperty(p, id); + } + + /** Registers a property with this class and associates the argument id with it. + * It does the same as {@link #registerProperty(Property, Object)}. The first three + * arguments are used to construct a Property object. The value for the Property.Handle + * is generated and does not have to be specified here. Use this method for registering + * a property if you do not care about the Property's handles. + * Registration has to occur during + * initialization of the inheriting class (i.e. within the constructor). + * @param name The property's name (Property.Name). + * @param type The property's type (Property.Type). + * @param attributes The property's attributes (Property.Attributes). + * @param id Identifies the property's storage. + */ + protected void registerProperty(String name, Type type, short attributes, Object id) + { + Property p= new Property(name, lastHandle++, type, attributes); + registerProperty(p, id); + } + + /** Registers a property with this class. This method expects that property values + * are stored in member variables as is the case if the methods convertPropertyValue, + * setPropertyValueNoBroadcast and getPropertyValue(Property) are not overridden. + * It is presumed that the type of the member variable + * corresponds Property.Type. For example, if the TypeClass of Property.Type is to be + * a TypeClass.SHORT then the member must be a short or java.lang.Short. + * The handle for the property is generated.<br> + * If there is no member with the specified name or if the member has an incompatible type + * then a com.sun.star.uno.RuntimeException is thrown. + * @param propertyName The name of the property. + * @param memberName The name of the member variable that holds the value of the property. + * @param attributes The property attributes. + */ + protected void registerProperty(String propertyName, String memberName, short attributes) + { + Field propField= null; + try + { + propField= getClass().getDeclaredField(memberName); + } + catch (NoSuchFieldException e) + { + throw new com.sun.star.uno.RuntimeException(e, "there is no member variable: " + memberName); + } + Class cl= propField.getType(); + Type t= new Type(cl); + if (t.getTypeClass() != TypeClass.UNKNOWN) + { + Property p= new Property(propertyName, lastHandle++, t, attributes); + registerProperty(p,memberName); + } + else + throw new com.sun.star.uno.RuntimeException("the member has an unknown type: " + memberName); + } + + /** Registers a property with this class. + * It is presumed that the name of property is equal to the name of the member variable + * that holds the property value. + * @param propertyName The name of the property and the member variable that holds the property's value. + * @param attributes The property attributes. + * @see #registerProperty(String, String, short) + */ + protected void registerProperty(String propertyName, short attributes) + { + registerProperty(propertyName, propertyName, attributes); + } + + + + /** Returns the Property object for a given property name or null if that property does + * not exists (i.e. it has not been registered). Override this method + * if you want to implement your own mapping from property names to Property objects. + * Then you also have to override {@link #initMappings}, {@link #getProperties()} and + * {@link #putProperty(Property)}. + * @param propertyName The name of the property (Property.Name) + * @return The Property object with the name <em>propertyName</em>. + */ + protected Property getProperty(String propertyName) + { + return _nameToPropertyMap.get(propertyName); + } + + /** Returns the Property object with a handle (Property.Handle) as specified by the argument + * <em>nHandle</em>. The method returns null if there is no such property (i.e. it has not + * been registered). Override this method if you want to implement your own mapping from handles + * to Property objects. Then you also have to override {@link #initMappings}, {@link #putProperty(Property)}. + * @param nHandle The handle of the property (Property.Handle). + * @return The Property object with the handle <em>nHandle</em> + */ + protected Property getPropertyByHandle(int nHandle) + { + return _handleToPropertyMap.get(Integer.valueOf(nHandle)); + } + + /** Returns an array of all Property objects or an array of length null if there + * are no properties. Override this method if you want to implement your own mapping from names + * to Property objects. Then you also have to override {@link #initMappings}, {@link #getProperty(String)} and + * {@link #putProperty}. + * @return Array of all Property objects. + */ + protected Property[] getProperties() + { + if (arProperties == null) + { + Collection<Property> values= _nameToPropertyMap.values(); + arProperties= values.toArray(new Property[_nameToPropertyMap.size()]); + } + return arProperties; + } + + /** Stores a Property object so that it can be retrieved subsequently by + * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}. + * Override this method if you want to implement your own mapping from handles + * to Property objects and names to Property objects. Then you also need to override {@link #initMappings}, + * {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}. + * @param prop The Property object that is to be stored. + */ + protected void putProperty(Property prop) + { + _nameToPropertyMap.put(prop.Name, prop); + if (prop.Handle != -1) + _handleToPropertyMap.put(Integer.valueOf(prop.Handle), prop); + } + + /** Assigns an identifier object to a Property object so that the identifier + * can be obtained by {@link #getPropertyId getPropertyId} later on. The identifier + * is used to specify a certain storage for the property's value. If you do not + * override {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} or {@link #getPropertyValue(Property)} + * then the argument <em>id</em> has to be a String that equals the name of + * the member variable that holds the Property's value. + * Override this method if you want to implement your own mapping from Property objects to ids or + * if you need ids of a type other than String. + * Then you also need to override {@link #initMappings initMappings} and {@link #getPropertyId getPropertyId}. + * @param prop The Property object that is being assigned an id. + * @param id The object which identifies the storage used for the property's value. + * @see #registerProperty(Property, Object) + */ + protected void assignPropertyId(Property prop, Object id) + { + if (id instanceof String && ((String) id).length() != 0) + _propertyToIdMap.put(prop, id); + } + + /** Returns the identifier object for a certain Property. The object must have been + * previously assigned to the Property object by {@link #assignPropertyId assignPropertyId}. + * Override this method if you want to implement your own mapping from Property objects to ids. + * Then you also need to override {@link #initMappings initMappings} and {@link #assignPropertyId assignPropertyId}. + * @param prop The property for which the id is to be retrieved. + * @return The id object that identifies the storage used for the property's value. + * @see #registerProperty(Property, Object) + */ + protected Object getPropertyId(Property prop) + { + return _propertyToIdMap.get(prop); + } + + /** Initializes data structures used for mappings of property names to property object, + * property handles to property objects and property objects to id objects. + * Override this method if you want to implement your own mappings. Then you also need to + * override {@link #putProperty putProperty},{@link #getProperty getProperty}, {@link #getPropertyByHandle}, + * {@link #assignPropertyId assignPropertyId} and {@link #getPropertyId getPropertyId}. + */ + protected void initMappings() + { + _nameToPropertyMap= new HashMap<String,Property>(); + _handleToPropertyMap= new HashMap<Integer,Property>(); + _propertyToIdMap= new HashMap<Property,Object>(); + } + + /** Makes sure that listeners which are kept in aBoundLC (XPropertyChangeListener) and aVetoableLC + * (XVetoableChangeListener) receive a disposing call. Also those listeners are released. + */ + @Override + protected void postDisposing() + { + // Create an event with this as sender + EventObject aEvt= new EventObject(this); + + // inform all listeners to release this object + aBoundLC.disposeAndClear(aEvt); + aVetoableLC.disposeAndClear(aEvt); + } + + //XPropertySet ---------------------------------------------------- + synchronized public void addPropertyChangeListener(String str, XPropertyChangeListener xPropertyChangeListener) + throws UnknownPropertyException, WrappedTargetException + { + // only add listeners if you are not disposed + if (! bInDispose && ! bDisposed) + { + if (str.length() > 0) + { + Property prop= getProperty(str); + if (prop == null) + throw new UnknownPropertyException("Property " + str + " is unknown"); + + // Add listener for a certain property + if ((prop.Attributes & PropertyAttribute.BOUND) > 0) + aBoundLC.addInterface(str, xPropertyChangeListener); + else + //ignore silently + return; + } + else + // Add listener for all properties + listenerContainer.addInterface(XPropertyChangeListener.class, xPropertyChangeListener); + } + } + //XPropertySet ---------------------------------------------------- + synchronized public void addVetoableChangeListener(String str, com.sun.star.beans.XVetoableChangeListener xVetoableChangeListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException + { + // only add listeners if you are not disposed + if (! bInDispose && ! bDisposed) + { + if (str.length() > 0) + { + Property prop= getProperty(str); + if (prop == null) + throw new UnknownPropertyException("Property " + str + " is unknown"); + + // Add listener for a certain property + if ((prop.Attributes & PropertyAttribute.CONSTRAINED) > 0) + aVetoableLC.addInterface(str, xVetoableChangeListener); + else + //ignore silently + return; + } + else + // Add listener for all properties + listenerContainer.addInterface(XVetoableChangeListener.class, xVetoableChangeListener); + } + } + //XPropertySet ---------------------------------------------------- + public synchronized com.sun.star.beans.XPropertySetInfo getPropertySetInfo() + { + if (propertySetInfo == null) + propertySetInfo= new PropertySetInfo(); + return propertySetInfo; + } + //XPropertySet ---------------------------------------------------- + public Object getPropertyValue(String name) throws UnknownPropertyException, WrappedTargetException + { + Object ret= null; + if (bInDispose || bDisposed) + throw new com.sun.star.lang.DisposedException("The component has been disposed already"); + + Property prop= getProperty(name); + if (prop == null) + throw new UnknownPropertyException("The property " + name + " is unknown"); + + synchronized (this) + { + ret= getPropertyValue(prop); + } + // null must not be returned. Either a void any is returned or an any containing + // an interface type and a null reference. + if (ret == null) + { + if (prop.Type.getTypeClass() == TypeClass.INTERFACE) + ret= new Any(prop.Type, null); + else + ret= new Any(new Type(void.class), null); + } + return ret; + } + + //XPropertySet ---------------------------------------------------- + synchronized public void removePropertyChangeListener(String propName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException + { // all listeners are automatically released in a dispose call + if (!bInDispose && !bDisposed) + { + if (propName.length() > 0) + { + Property prop = getProperty(propName); + if (prop == null) + throw new UnknownPropertyException("Property " + propName + " is unknown"); + aBoundLC.removeInterface(propName, listener); + } + else + listenerContainer.removeInterface(XPropertyChangeListener.class, listener); + } + } + + //XPropertySet ---------------------------------------------------- + synchronized public void removeVetoableChangeListener(String propName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException + {// all listeners are automatically released in a dispose call + if (!bInDispose && !bDisposed) + { + if (propName.length() > 0) + { + Property prop = getProperty(propName); + if (prop == null) + throw new UnknownPropertyException("Property " + propName + " is unknown"); + aVetoableLC.removeInterface(propName, listener); + } + else + listenerContainer.removeInterface(XVetoableChangeListener.class, listener); + } + } + + //XPropertySet ---------------------------------------------------- + /** Sets the value of a property. + * The idl description for this interfaces, stipulates that the argument value is an Any. Since a java.lang.Object + * reference has the same meaning as an Any this function accepts + * java anys (com.sun.star.uno.Any) and all other appropriate objects as arguments. The value argument can be one + * of these: + * <ul> + * <li>java.lang.Boolean</li> + * <li>java.lang.Character</li> + * <li>java.lang.Byte</li> + * <li>java.lang.Short</li> + * <li>java.lang.Integer</li> + * <li>java.lang.Long</li> + * <li>java.lang.Float</li> + * <li>java.lang.Double</li> + * <li>String</li> + * <li>com.sun.star.uno.Type</li> + * <li><em>objects which implement UNO interfaces</em></li> + * <li><em>arrays which contain elements of the types above</em></li> + * <li>com.sun.star.uno.Any containing an instance of one of the above types</li> + * </ul> + * + * Properties can have the attribute com.sun.star.beans.PropertyAttribute.MAYBEVOID, which means that the value + * (not the type) can be void. In order to assign a void value to a property one can either pass an Any which + * contains a null reference or pass null directly. In both cases the null reference is only accepted if + * the PropertyAttribute.MAYBEVOID attribute is set for the property. + * + * Properties which have the attribute MAYBEVOID set (Property.Attributes) can have a void value. The following + * considerations presume that the Property has that attribute set. Further, when mentioning an Any's value we + * actually refer to the object returned by Any.getObject. + * If the argument <em>value</em> is null, or it is an Any whose value is null (but with a valid Type) + * then the member variable used for storing the property's value is set to null. + * Therefore those properties can only be stored in objects + * and primitive types are not allowed (one can use the wrapper classes instead,e.g. java.lang.Byte) . + * If a property's value is kept in a member variable of type Any and that reference is still null + * then when setPropertyValue is called with + * <em>value</em> = null then the member variable is assigned an Any with type void and a null value. + * Or if the argument is an Any with a null value then it is assigned to the member variable. + * Further, if the variable already + * references an Any and setPropertyValue is called with <em>value</em> = null, then the variable is assigned + * a new Any with the same type as the previously referenced Any and with a null value. + * @param name The name of the property. + * @param value The new value of the property. + * * */ + public void setPropertyValue(String name, Object value) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + Property prop= getProperty(name); + if (prop == null) + throw new UnknownPropertyException("Property " + name + " is unknown"); + setPropertyValue(prop, value); + } + + /** Sets the value of a property. It checks if the property's attributes (READONLY,MAYBEVOID), allow that the + * new value can be set. It also causes the notification of listeners. + * @param prop The property whose value is to be set. + * @param value The new value for the property. + * + * @throws UnknownPropertyException + * See com.sun.star.beans.XPropertySet + * @throws PropertyVetoException + * See com.sun.star.beans.XPropertySet + * @throws WrappedTargetException + * See com.sun.star.beans.XPropertySet + */ + protected void setPropertyValue(Property prop, Object value) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + if ((prop.Attributes & PropertyAttribute.READONLY) == PropertyAttribute.READONLY) + throw new com.sun.star.beans.PropertyVetoException(); + // The value may be null only if MAYBEVOID attribute is set + boolean bVoidValue; + if (value instanceof Any) + bVoidValue= ((Any) value).getObject() == null; + else + bVoidValue= value == null; + if (bVoidValue && (prop.Attributes & PropertyAttribute.MAYBEVOID) == 0) + throw new com.sun.star.lang.IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!"); + if (bInDispose || bDisposed) + throw new DisposedException("Component is already disposed"); + + //Check if the argument is allowed + boolean bValueOk; + if (value instanceof Any) + bValueOk= checkType(((Any) value).getObject()); + else + bValueOk= checkType(value); + if (! bValueOk) + throw new com.sun.star.lang.IllegalArgumentException("No valid UNO type"); + + + boolean bConversionOk= false; + Object[] outConvertedVal= new Object[1]; + Object[] outOldValue= new Object[1]; + synchronized (this) + { + bConversionOk= convertPropertyValue(prop, outConvertedVal, outOldValue, value); + } + + //The next step following the conversion is to set the new value of the property. Prior to this + // the XVetoableChangeListener s have to be notified. + if (bConversionOk) + { + // If the property is CONSTRAINED, then we must notify XVetoableChangeListener. The listener can throw a com.sun.star.lang.beans.PropertyVetoException which + // will cause this method to return (the exception is not caught here). + fire( new Property[]{prop}, outConvertedVal, outOldValue, true); + + synchronized (this) + { + setPropertyValueNoBroadcast(prop, outConvertedVal[0]); + } + // fire a change event (XPropertyChangeListener, PropertyAttribute.BOUND + fire( new Property[]{prop}, outConvertedVal, outOldValue, false); + } + } + + /** Converts a value in a way so that it is appropriate for storing as a property value, that is + * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} can process the value without any further + * conversion. This implementation presumes that + * the values are stored in member variables of the furthest inheriting class. For example, + * class A inherits this class then members of class A + * can hold property values. If there is a class B which inherits A then only members of B can hold + * property values. The variables must be public. A property must have been registered (e.g. by + * {@link #registerProperty(Property, Object)} in order for this method to work. The identifier argument (type Object) + * used in the registerProperty methods must + * be a String, which is, the name of the member variable that holds the property value. + * If one opts to store values differently then one may override + * this method, as well as {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and + * {@link #getPropertyValue(Property) getPropertyValue(Property)}. + * This method is always called as a result of a call to one of the setter methods, such as + * {@link #setPropertyValue(String,Object) XPropertySet.setPropertyValue}, + * {@link #setFastPropertyValue XFastPropertySet.setFastPropertyValue} + * and {@link #setPropertyValues XMultiPropertySet.setPropertyValues}. + * If this method fails, that is, it returns false or throws an exception, then no listeners are notified and the + * property value, that was intended to be changed, remains untouched. + * + * This method does not have to deal with property attributes, such as + * PropertyAttribute.READONLY or PropertyAttribute.MAYBEVOID. The processing of these attributes occurs + * in the calling methods. + * + * Only if this method returns successfully further processing, such + * as listener notification and finally the modification of the property's value, will occur. + * + * The actual modification of a property's value is done by {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} + * which is called subsequent to convertPropertyValue. + *<p> + * This method converts values by help of the com.sun.star.uno.AnyConverter which only does a few widening + * conversions on integer types and floating point types. For example, there is the property PropA with a Type equivalent + * to int.class and the + * value of the property is to be stored in a member variable of type int with name intProp. Then setPropertyValue is + * called: + * <pre> + * set.setPropertyValue( "PropA", Byte.valueOf( (byte)111)); + * </pre> + * At some point setPropertyValue will call convertPropertyValue and pass in the Byte object. Since we allow + * that Byte values can be used with the property and know that the value is to be stored in intProp (type int) + * we convert the Byte object into an Integer object which is then returned in the out-parameter <em>newVal</em>. This + * conversion is actually performed by the AnyConverter. Later + * the setPropertyValueNoBroadcast is called with that Integer object and the int value can be easily extracted + * from the object and be assigned to the member intProp. + * <p> + * The method handles Any arguments the same as Object arguments. That is, the <em>setVal</em> argument can + * be a java.lang.Boolean or a com.sun.star.uno.Any containing a java.lang.Boolean. Likewise, a member + * containing a property value can be a com.sun.star.uno.Any or a java.lang.Object. + * Then, no conversion is necessary, since they can hold all possible values. However, if + * the member is an Object and <em>setVal</em> is an Any then the object contained in the any is assigned to + * the member. The extra type information which exists as Type object in the Any will get lost. If this is not + * intended then use an Any variable rather than an Object. + * + * If a member is an Object or Any and the argument <em>setVal</em> is an Object, other than String or array, + * then it is presumed to be a UNO object and queried for XInterface. If successful, the out-param <em>newVal</em> + * returns the XInterface. + * + * If a member is a UNO interface, then <em>setVal</em> is queried for this interface and the result is returned. + * If <em>setVal</em> is null then <em>newVal</em> will be null too after return. + * <p> + * If a property value is stored using a primitive type the out-parameters + * <em>curVal</em> and <em>newVal</em> contain the respective wrapper class (e.g.java.lang.Byte, etc.). + * curVal is used in calls to the XVetoableChangeListener and XPropertyChangeListener. + * + * @param property - in-param property for which the data is to be converted. + * @param newVal - out-param which contains the converted value on return. + * @param curVal - out-param the current value of the property. It is used in calls to the + * XVetoableChangeListener and XPropertyChangeListener. + * @param setVal - in-param. The value that is to be converted so that it matches Property and the internally used + * dataformat for that property. + * @return true - Conversion was successful. <em>newVal</em> contains a valid value for the property. false - + * conversion failed for some reason. + * + * @throws UnknownPropertyException + * See com.sun.star.beans.XPropertySet + * @throws com.sun.star.lang.IllegalArgumentException The value provided is unfit for the property. + * @throws com.sun.star.lang.WrappedTargetException - An exception occurred during the conversion, that is to be made known + * to the caller. + */ + protected boolean convertPropertyValue(Property property, Object[] newVal, Object[]curVal, Object setVal) + throws com.sun.star.lang.IllegalArgumentException, WrappedTargetException, UnknownPropertyException + { + boolean ret= true; + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + { + curVal[0]= propField.get(this); + Class memberClass= propField.getType(); + + // MAYBEVOID: if setVal == null or it is an Any and getObject returns null, then a void value is to be set + // This works only if there are no primitive types. For those we use the respective wrapper classes. + // In this implementation, a null reference means void value. + boolean bVoidValue= false; + boolean bAnyVal= setVal instanceof Any; + if (bAnyVal) + bVoidValue= ((Any) setVal).getObject() == null; + else + bVoidValue= setVal == null; + if (bVoidValue && memberClass.isPrimitive()) + throw new com.sun.star.lang.IllegalArgumentException("The implementation does not support the MAYBEVOID attribute for this property"); + + Object convObj= null; + //The member that keeps the value of the Property is an Any. It can contain all possible + //types, therefore a conversion is not necessary. + if (memberClass.equals(Any.class)) + { + if (bAnyVal) + //parameter setVal is also an Any and can be used without further processing + convObj= setVal; + else + { + // Parameter setVal is not an Any. We need to construct an Any that contains + // the argument setVal. + // If setVal is an interface implementation then, we cannot construct the + // Any with setVal.getClass(), because the Any.Type._typeClass would be TypeClass.UNKNOWN. + // We try to get an XInterface of setVal and set an XInterface type. + if (setVal instanceof XInterface) + { + XInterface xint= UnoRuntime.queryInterface(XInterface.class, setVal); + if (xint != null) + convObj= new Any(new Type(XInterface.class), xint); + } + // The member is an any, and the past in argument was null reference (MAYBEVOID is set) + else if (setVal == null) + { + // if the any member is still null we create a void any + if (curVal[0] == null) + convObj= new Any(new Type(), null); + else + { + //otherwise we create an Any with the same type as a value of null; + convObj= new Any( ((Any)curVal[0]).getType(), null); + } + } + else + convObj= new Any(new Type(setVal.getClass()), setVal); + } + } + else + convObj= convert(memberClass, setVal); + newVal[0]= convObj; + } + } + else + throw new UnknownPropertyException("Property " + property.Name + " is unknown"); + } + catch (java.lang.NoSuchFieldException e) + { + throw new WrappedTargetException(e, "Field does not exist", this, e); + } + catch (java.lang.IllegalAccessException e) + { + throw new WrappedTargetException(e, "", this ,e); + } + return ret; + } + + private boolean checkType(Object obj) + { + return obj == null + || obj instanceof Boolean + || obj instanceof Character + || obj instanceof Number + || obj instanceof String + || obj instanceof XInterface + || obj instanceof Type + || obj instanceof com.sun.star.uno.Enum + || obj.getClass().isArray(); + } + + // Param object can be an Any or other object. If obj is null then the return value is null + private Object convert( Class cl, Object obj) throws com.sun.star.lang.IllegalArgumentException + { + Object retVal= null; + //The member that keeps the value of the Property is an Object.Objects are similar to Anys in that they can + // hold all types. + if (obj == null || (obj instanceof Any && ((Any) obj).getObject() == null)) + {} + else if(cl.equals(Object.class)) + { + if (obj instanceof Any) + obj= ((Any) obj).getObject(); + retVal= obj; + } + else if(cl.equals(boolean.class)) + retVal= Boolean.valueOf(AnyConverter.toBoolean(obj)); + else if (cl.equals(char.class)) + retVal= new Character(AnyConverter.toChar(obj)); + else if (cl.equals(byte.class)) + retVal= Byte.valueOf(AnyConverter.toByte(obj)); + else if (cl.equals(short.class)) + retVal= Short.valueOf(AnyConverter.toShort(obj)); + else if (cl.equals(int.class)) + retVal= Integer.valueOf(AnyConverter.toInt(obj)); + else if (cl.equals(long.class)) + retVal= Long.valueOf(AnyConverter.toLong(obj)); + else if (cl.equals(float.class)) + retVal= new Float(AnyConverter.toFloat(obj)); + else if (cl.equals(double.class)) + retVal= new Double(AnyConverter.toDouble(obj)); + else if (cl.equals(String.class)) + retVal= AnyConverter.toString(obj); + else if (cl.isArray()) + retVal= AnyConverter.toArray(obj); + else if (cl.equals(Type.class)) + retVal= AnyConverter.toType(obj); + else if (cl.equals(Boolean.class)) + retVal= Boolean.valueOf(AnyConverter.toBoolean(obj)); + else if (cl.equals(Character.class)) + retVal= new Character(AnyConverter.toChar(obj)); + else if (cl.equals(Byte.class)) + retVal= Byte.valueOf(AnyConverter.toByte(obj)); + else if (cl.equals(Short.class)) + retVal= Short.valueOf(AnyConverter.toShort(obj)); + else if (cl.equals(Integer.class)) + retVal= Integer.valueOf(AnyConverter.toInt(obj)); + else if (cl.equals(Long.class)) + retVal= Long.valueOf(AnyConverter.toLong(obj)); + else if (cl.equals(Float.class)) + retVal= new Float(AnyConverter.toFloat(obj)); + else if (cl.equals(Double.class)) + retVal= new Double(AnyConverter.toDouble(obj)); + else if (XInterface.class.isAssignableFrom(cl)) + retVal= AnyConverter.toObject(new Type(cl), obj); + else if (com.sun.star.uno.Enum.class.isAssignableFrom(cl)) + retVal= AnyConverter.toObject(new Type(cl), obj); + else + throw new com.sun.star.lang.IllegalArgumentException("Could not convert the argument"); + return retVal; + } + + /** Sets the value of a property. In this implementation property values are stored in member variables + * (see {@link #convertPropertyValue convertPropertyValue} Notification of property listeners + * does not occur in this method. By overriding this method one can take full control about how property values + * are stored. But then, the {@link #convertPropertyValue convertPropertyValue} and + * {@link #getPropertyValue(Property)} must be overridden too. + * + * A Property with the MAYBEVOID attribute set, is stored as null value. Therefore the member variable must be + * an Object in order to make use of the property attribute. An exception is Any. The Any variable can be initially null, but + * once it is set the reference will not become null again. If the value is to be set to + * void then a new Any will be stored + * with a valid type but without a value (i.e. Any.getObject returns null). + * If a property has the READONLY attribute set, and one of the setter methods, such as setPropertyValue, has been + * called, then this method is not going to be called. + * @param property the property for which the new value is set + * @param newVal the new value for the property. + * @throws com.sun.star.lang.WrappedTargetException An exception, which has to be made known to the caller, + * occurred during the setting of the value. + */ + protected void setPropertyValueNoBroadcast(Property property, Object newVal) + throws WrappedTargetException + { + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + propField.set(this, newVal); + } + } + catch(java.lang.Exception e) + { + throw new WrappedTargetException(e, "PropertySet.setPropertyValueNoBroadcast", this, e); + } + } + /** Retrieves the value of a property. This implementation presumes that the values are stored in member variables + * of the furthest inheriting class (see {@link #convertPropertyValue convertPropertyValue}) and that the + * variables are public. The property must have + * been registered, for example by {@link #registerProperty(Property, Object)}. The identifier Object argument + * must have been a String which was the name of the member variable holding the property value. + * When properties are to be stored differently one has to override this method as well as + * {@link #convertPropertyValue} and {@link #setPropertyValueNoBroadcast}. <br> + * If a value is stored in a variable of a primitive type then this method returns an instance of the respective + * wrapper class (e.g. java.lang.Boolean). + * @param property The property for which the value is to be retrieved. + * @return The value of the property. + */ + protected Object getPropertyValue(Property property) + { + Object ret= null; + try + { + // get the member name + String sMember= (String) getPropertyId(property); + if (sMember != null) + { + // use reflection to obtain the field that holds the property value + // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to + // also get inherited fields, but only those which are public. + Field propField= getClass().getDeclaredField(sMember); + if (propField != null) + ret= propField.get(this); + } + } + catch(java.lang.NoSuchFieldException e) + { + throw new java.lang.RuntimeException(e); + } + catch(java.lang.IllegalAccessException e) + { + throw new java.lang.RuntimeException(e); + } + return ret; + } + + /** + * This method fires events to XPropertyChangeListener,XVetoableChangeListener and + * XPropertiesChangeListener event sinks. + * To distinguish what listeners are to be called the argument <em>bVetoable</em> is to be set to true if + * a XVetoableChangeListener is meant. For XPropertyChangeListener and XPropertiesChangeListener + * it is to be set to false. + * + * @param properties Properties which will be or have been affected. + * @param newValues the new values of the properties. + * @param oldValues the old values of the properties. + * @param bVetoable true means fire to VetoableChangeListener, false means fire to + * XPropertyChangedListener and XMultiPropertyChangedListener. + * + * @throws PropertyVetoException + * if a vetoable listener throws it. + */ + protected void fire( + Property[] properties, + Object[] newValues, + Object[] oldValues, + boolean bVetoable ) throws PropertyVetoException + { + // Only fire, if one or more properties changed + int nNumProps= properties.length; + if (nNumProps > 0) + { + PropertyChangeEvent[] arEvts= new PropertyChangeEvent[nNumProps]; + int nAffectedProps= 0; + // Loop over all changed properties to fill the event struct + for (int i= 0; i < nNumProps; i++) + { + if ((bVetoable && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) > 0) + || (!bVetoable && (properties[i].Attributes & PropertyAttribute.BOUND) > 0)) + { + arEvts[i]= new PropertyChangeEvent(this, properties[i].Name, false, + properties[i].Handle, oldValues[i], newValues[i]); + nAffectedProps++; + } + } + // fire the events for all changed properties + for (int i= 0; i < nAffectedProps; i++) + { + // get the listener container for the property name + InterfaceContainer lc; + if (bVetoable) + lc= aVetoableLC.getContainer(arEvts[i].PropertyName); + else + lc= aBoundLC.getContainer(arEvts[i].PropertyName); + Iterator it = lc != null ? lc.iterator() : null; + if (it != null) + { + while( it.hasNext()) + { + Object listener= it.next(); + if (bVetoable) + ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]); + else + ((XPropertyChangeListener) listener).propertyChange(arEvts[i]); + } + } + // broadcast to all listeners with "" property name + if(bVetoable) + lc= listenerContainer.getContainer(XVetoableChangeListener.class); + else + lc= listenerContainer.getContainer(XPropertyChangeListener.class); + it = lc != null ? lc.iterator() : null; + if (it != null) + { + while(it.hasNext() ) + { + Object listener= it.next(); + if( bVetoable ) // fire change Events? + ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]); + else + ((XPropertyChangeListener) listener).propertyChange(arEvts[i]); + } + } + } + // fire at XPropertiesChangeListeners + // if nAffectedProps == 0 then there are no BOUND properties + if (!bVetoable && nAffectedProps > 0) + { + + PropertyChangeEvent[] arReduced= new PropertyChangeEvent[nAffectedProps]; + System.arraycopy(arEvts, 0, arReduced, 0, nAffectedProps); + InterfaceContainer lc= listenerContainer.getContainer(XPropertiesChangeListener.class); + Iterator it = lc != null ? lc.iterator() : null; + if (it != null) + { + while (it.hasNext()) + { + XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next(); + // fire the whole event sequence to the XPropertiesChangeListener's + listener.propertiesChange( arEvts ); + } + } + } + } + } + // XFastPropertySet-------------------------------------------------------------------------------- + public void setFastPropertyValue(int nHandle, Object aValue ) throws UnknownPropertyException, + PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + Property prop= getPropertyByHandle(nHandle); + if (prop == null) + throw new UnknownPropertyException(" The property with handle : " + nHandle +" is unknown"); + setPropertyValue(prop, aValue); + } + + // XFastPropertySet -------------------------------------------------------------------------------- + public Object getFastPropertyValue(int nHandle ) throws UnknownPropertyException, + WrappedTargetException + { + Property prop= getPropertyByHandle(nHandle); + if (prop == null) + throw new UnknownPropertyException("The property with handle : " + nHandle + " is unknown"); + return getPropertyValue(prop); + } + + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void addPropertiesChangeListener(String[] propNames, XPropertiesChangeListener listener) + { + listenerContainer.addInterface(XPropertiesChangeListener.class, listener); + } + + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void firePropertiesChangeEvent(String[] propNames, XPropertiesChangeListener listener) + { + // Build the events. + PropertyChangeEvent[] arEvents= new PropertyChangeEvent[propNames.length]; + int eventCount= 0; + // get a snapshot of the current property values + synchronized (this) + { + for (int i= 0; i < propNames.length; i++) + { + Property prop= getProperty(propNames[i]); + if (prop != null) + { + Object value= null; + try + { + value= getPropertyValue(prop); + } + catch(Exception e) + { + continue; + } + arEvents[eventCount]= new PropertyChangeEvent(this, prop.Name, + false, prop.Handle, value, value); + eventCount++; + } + } + } + + // fire events from unsynchronized section so as to prevent deadlocks + if (eventCount > 0) + { + // Reallocate the array of the events if necessary + if (arEvents.length != eventCount) + { + PropertyChangeEvent[] arPropsTmp= new PropertyChangeEvent[eventCount]; + System.arraycopy(arEvents, 0, arPropsTmp, 0, eventCount); + arEvents= arPropsTmp; + } + listener.propertiesChange(arEvents); + } + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + /** If a value for a property could not be retrieved then the respective element in the returned + * array has the value null. + */ + public Object[] getPropertyValues(String[] propNames) + { + Object[] arValues= new Object[propNames.length]; + synchronized (this) + { + for (int i= 0; i < propNames.length; i++) + { + Object value= null; + try + { + value= getPropertyValue(propNames[i]); + } + catch (Exception e) + { + } + arValues[i]= value; + } + } + return arValues; + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + public void removePropertiesChangeListener(XPropertiesChangeListener xPropertiesChangeListener) + { + listenerContainer.removeInterface(XPropertiesChangeListener.class, xPropertiesChangeListener); + } + // XMultiPropertySet ----------------------------------------------------------------------------------- + /** If the array of property names contains an unknown property then it will be ignored. + */ + public void setPropertyValues(String[] propNames, Object[] values) throws PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException + { + for (int i= 0; i < propNames.length; i++) + { + try + { + setPropertyValue(propNames[i], values[i]); + } + catch (UnknownPropertyException e) + { + continue; + } + + } + } + + private class PropertySetInfo implements XPropertySetInfo + { + public com.sun.star.beans.Property[] getProperties() + { + return PropertySet.this.getProperties(); + } + + public com.sun.star.beans.Property getPropertyByName(String name) throws UnknownPropertyException + { + return getProperty(name); + } + + public boolean hasPropertyByName(String name) + { + return getProperty(name) != null; + } + + } +} + + + + + diff --git a/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java new file mode 100644 index 000000000..0c050d676 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/PropertySetMixin.java @@ -0,0 +1,1111 @@ +/* + * 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 com.sun.star.beans.Property; +import com.sun.star.beans.PropertyAttribute; +import com.sun.star.beans.PropertyChangeEvent; +import com.sun.star.beans.PropertyState; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.PropertyVetoException; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertyChangeListener; +import com.sun.star.beans.XPropertySetInfo; +import com.sun.star.beans.XVetoableChangeListener; +import com.sun.star.container.NoSuchElementException; +import com.sun.star.container.XHierarchicalNameAccess; +import com.sun.star.lang.DisposedException; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.WrappedTargetRuntimeException; +import com.sun.star.reflection.XCompoundTypeDescription; +import com.sun.star.reflection.XIdlClass; +import com.sun.star.reflection.XIdlField2; +import com.sun.star.reflection.XIndirectTypeDescription; +import com.sun.star.reflection.XInterfaceAttributeTypeDescription2; +import com.sun.star.reflection.XInterfaceMemberTypeDescription; +import com.sun.star.reflection.XInterfaceTypeDescription2; +import com.sun.star.reflection.XStructTypeDescription; +import com.sun.star.reflection.XTypeDescription; +import com.sun.star.reflection.theCoreReflection; +import com.sun.star.uno.Any; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.TypeClass; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.XInterface; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +/** + A helper mixin to implement certain UNO interfaces related to property set + handling on top of the attributes of a given UNO interface type. + + <p>A client will mix in this class by keeping a reference to an instance of + this class, and forwarding all methods of (a subset of the interfaces) + <code>com.sun.star.beans.XPropertySet</code>, + <code>com.sun.star.beans.XFastPropertySet</code>, and + <code>com.sun.star.beans.XPropertyAccess</code> to it.</p> + + <p>Client code should not use the monitors associated with instances of this + class, as they are used for internal purposes.</p> + + @since UDK 3.2 +*/ +public final class PropertySetMixin { + /** + The constructor. + + @param context the component context used by this instance; must not be + null, and must supply the + <code>com.sun.star.reflection.theCoreReflection</code> and + <code>com.sun.star.reflection.theTypeDescriptionManager</code> singletons + + @param object the client UNO object into which this instance is mixed in; + must not be null, and must support the given <code>type</code> + + @param type the UNO interface type whose attributes are mapped to + properties; must not be null, and must represent a UNO interface type + + @param absentOptional a list of optional properties that are not present, + and should thus not be visible via + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>, + <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>, + <code>com.sun.star.beans.XPropertySet.removePropertyChangeListener<!-- + --></code>, + <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>, + and <code>com.sun.star.beans.XPropertySet.<!-- + -->removeVetoableChangeListener</code>; null is treated the same as an + empty list; if non-null, the given array must not be modified after it is + passed to this constructor. For consistency reasons, the given + <code>absentOptional</code> should only contain the names of attributes + that represent optional properties that are not present (that is, the + attribute getters and setters always throw a + <code>com.sun.star.beans.UnknownPropertyException</code>), and should + contain each such name only once. If an optional property is not present + (that is, the corresponding attribute getter and setter always throw a + <code>com.sun.star.beans.UnknownPropertyException</code>) but is not + contained in the given <code>absentOptional</code>, then it will be + visible via + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code> as a + <code>com.sun.star.beans.Property</code> with a set + <code>com.sun.star.beans.PropertyAttribute.OPTIONAL</code>. If the given + <code>object</code> does not implement + <code>com.sun.star.beans.XPropertySet</code>, then the given + <code>absentOptional</code> is effectively ignored and can be null or + empty. + */ + public PropertySetMixin( + XComponentContext context, XInterface object, Type type, + String[] absentOptional) + { + this.context = context; + this.object = object; + this.type = type; + this.absentOptional = absentOptional; + idlClass = getReflection(type.getTypeName()); + XTypeDescription ifc; + try { + XHierarchicalNameAccess xhna = UnoRuntime.queryInterface( + XHierarchicalNameAccess.class, + context.getValueByName( + "/singletons/com.sun.star.reflection." + + "theTypeDescriptionManager")); + ifc = UnoRuntime.queryInterface( + XTypeDescription.class, + xhna.getByHierarchicalName(type.getTypeName())); + } catch (NoSuchElementException e) { + throw new RuntimeException(e); + } + HashMap<String,PropertyData> map = new HashMap<String,PropertyData>(); + ArrayList<String> handleNames = new ArrayList<String>(); + initProperties(ifc, map, handleNames, new HashSet<String>()); + properties = map; + handleMap = handleNames.toArray(new String[handleNames.size()]); + } + + /** + A method used by clients when implementing UNO interface type attribute + setter functions. + + <p>First, this method checks whether this instance has already been + disposed (see {@link #dispose}), and throws a + <code>com.sun.star.beans.DisposedException</code> if applicable. For a + constrained attribute (whose setter can explicitly raise + <code>com.sun.star.beans.PropertyVetoException</code>), this method + notifies any <code>com.sun.star.beans.XVetoableChangeListener</code>s. + For a bound attribute, this method modifies the passed-in + <code>bound</code> so that it can afterwards be used to notify any + <code>com.sun.star.beans.XPropertyChangeListener</code>s. This method + should be called before storing the new attribute value, and + <code>bound.notifyListeners()</code> should be called exactly once after + storing the new attribute value (in case the attribute is bound; + otherwise, calling <code>bound.notifyListeners()</code> is ignored). + Furthermore, <code>bound.notifyListeners()</code> and this method have to + be called from the same thread.</p> + + @param propertyName the name of the property (which is the same as the + name of the attribute that is going to be set) + + @param oldValue the property value corresponding to the old attribute + value. This is only used as + <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code>, which is + rather useless, anyway (see “Using the Observer Pattern” in + <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> + <cite>OpenOffice.org Coding Guidelines</cite></a>). If the attribute + that is going to be set is neither bound nor constrained, or if + <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code> should not + be set, {@link Any#VOID} can be used instead. + + @param newValue the property value corresponding to the new + attribute value. This is only used as + <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code>, which is + rather useless, anyway (see “Using the Observer Pattern” in + <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> + <cite>OpenOffice.org Coding Guidelines</cite></a>), <em>unless</em> the + attribute that is going to be set is constrained. If the attribute + that is going to be set is neither bound nor constrained, or if it is + only bound but + <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code> should not + be set, {@link Any#VOID} can be used instead. + + @param bound a reference to a fresh {@link BoundListeners} instance + (which has not been passed to this method before, and on which + {@link BoundListeners#notifyListeners} has not yet been called); may only + be null if the attribute that is going to be set is not bound + + @throws PropertyVetoException if a vetoable listener throws it. + */ + public void prepareSet( + String propertyName, Object oldValue, Object newValue, + BoundListeners bound) + throws PropertyVetoException + { + // assert properties.get(propertyName) != null; + Property p = properties.get(propertyName).property; + ArrayList<XVetoableChangeListener> specificVeto = null; + ArrayList<XVetoableChangeListener> unspecificVeto = null; + synchronized (this) { + if (disposed) { + throw new DisposedException("disposed", object); + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + ArrayList<XVetoableChangeListener> o = vetoListeners.get(propertyName); + if (o != null) { + specificVeto = new ArrayList<XVetoableChangeListener>(o); + } + o = vetoListeners.get(""); + if (o != null) { + unspecificVeto = new ArrayList<XVetoableChangeListener>(o); + } + } + if ((p.Attributes & PropertyAttribute.BOUND) != 0) { + // assert bound != null; + ArrayList<XPropertyChangeListener> o = boundListeners.get(propertyName); + if (o != null) { + bound.specificListeners = new ArrayList<XPropertyChangeListener>(o); + } + o = boundListeners.get(""); + if (o != null) { + bound.unspecificListeners = new ArrayList<XPropertyChangeListener>(o); + } + } + } + if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { + PropertyChangeEvent event = new PropertyChangeEvent( + object, propertyName, false, p.Handle, oldValue, newValue); + if (specificVeto != null) { + for (Iterator<XVetoableChangeListener> i = specificVeto.iterator(); i.hasNext();) { + try { + i.next().vetoableChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificVeto != null) { + for (Iterator<XVetoableChangeListener> i = unspecificVeto.iterator(); i.hasNext();) { + try { + i.next().vetoableChange(event); + } catch (DisposedException e) {} + } + } + } + if ((p.Attributes & PropertyAttribute.BOUND) != 0) { + // assert bound != null; + bound.event = new PropertyChangeEvent( + object, propertyName, false, p.Handle, oldValue, newValue); + } + } + + /** + A simplified version of {@link #prepareSet(String, Object, Object, + PropertySetMixin.BoundListeners)}. + + <p>This method is useful for attributes that are not constrained.</p> + + @param propertyName the name of the property (which is the same as the + name of the attribute that is going to be set) + + @param bound a reference to a fresh {@link BoundListeners} instance + (which has not been passed to this method before, and on which + {@link BoundListeners#notifyListeners} has not yet been called); may only + be null if the attribute that is going to be set is not bound + */ + public void prepareSet(String propertyName, BoundListeners bound) { + try { + prepareSet(propertyName, Any.VOID, Any.VOID, bound); + } catch (PropertyVetoException e) { + throw new RuntimeException("unexpected " + e); + } + } + + /** + Marks this instance as being disposed. + + <p>See <code>com.sun.star.lang.XComponent</code> for the general concept + of disposing UNO objects. On the first call to this method, all + registered listeners + (<code>com.sun.star.beans.XPropertyChangeListener</code>s and + <code>com.sun.star.beans.XVetoableChangeListener</code>s) are notified of + the disposing source. Any subsequent calls to this method are + ignored.</p> + */ + public void dispose() { + HashMap<String,ArrayList<XPropertyChangeListener>> bound; + HashMap<String,ArrayList<XVetoableChangeListener>> veto; + synchronized (this) { + bound = boundListeners; + boundListeners = null; + veto = vetoListeners; + vetoListeners = null; + disposed = true; + } + EventObject event = new EventObject(object); + if (bound != null) { + for (Iterator<ArrayList<XPropertyChangeListener>> i = bound.values().iterator(); i.hasNext();) { + for (Iterator<XPropertyChangeListener> j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + if (veto != null) { + for (Iterator<ArrayList<XVetoableChangeListener>> i = veto.values().iterator(); i.hasNext();) { + for (Iterator<XVetoableChangeListener> j = i.next().iterator(); j.hasNext();) + { + j.next().disposing(event); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>. + @return See com.sun.star.beans.XPropertySet + */ + public XPropertySetInfo getPropertySetInfo() { + return new Info(properties); + } + + /** + Implements <code>com.sun.star.beans.XPropertySet.setPropertyValue</code>. + @param propertyName + See com.sun.star.beans.XPropertySet + @param value + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws PropertyVetoException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void setPropertyValue(String propertyName, Object value) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + setProperty(propertyName, value, false, false, (short) 1); + } + + /** + Implements <code>com.sun.star.beans.XPropertySet.getPropertyValue</code>. + @param propertyName + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + @return + See com.sun.star.beans.XPropertySet + */ + public Object getPropertyValue(String propertyName) + throws UnknownPropertyException, WrappedTargetException + { + return getProperty(propertyName, null); + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>. + + <p>If a listener is added more than once, it will receive all relevant + notifications multiple times.</p> + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void addPropertyChangeListener( + String propertyName, XPropertyChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + boolean disp; + synchronized (this) { + disp = disposed; + if (!disp) { + ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName); + if (v == null) { + v = new ArrayList<XPropertyChangeListener>(); + boundListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements <code> + com.sun.star.beans.XPropertySet.removePropertyChangeListener</code>. + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void removePropertyChangeListener( + String propertyName, XPropertyChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + synchronized (this) { + if (boundListeners != null) { + ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>. + + <p>If a listener is added more than once, it will receive all relevant + notifications multiple times.</p> + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void addVetoableChangeListener( + String propertyName, XVetoableChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + boolean disp; + synchronized (this) { + disp = disposed; + if (!disp) { + ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName); + if (v == null) { + v = new ArrayList<XVetoableChangeListener>(); + vetoListeners.put(propertyName, v); + } + v.add(listener); + } + } + if (disp) { + listener.disposing(new EventObject(object)); + } + } + + /** + Implements <code> + com.sun.star.beans.XPropertySet.removeVetoableChangeListener</code>. + + @param propertyName + See com.sun.star.beans.XPropertySet + @param listener + See com.sun.star.beans.XPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XPropertySet + */ + public void removeVetoableChangeListener( + String propertyName, XVetoableChangeListener listener) + throws UnknownPropertyException, WrappedTargetException + { + // assert listener != null; + checkUnknown(propertyName); + synchronized (this) { + if (vetoListeners != null) { + ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName); + if (v != null) { + v.remove(listener); + } + } + } + } + + /** + Implements + <code>com.sun.star.beans.XFastPropertySet.setFastPropertyValue</code>. + + @param handle + See com.sun.star.beans.XFastPropertySet + @param value + See com.sun.star.beans.XFastPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XFastPropertySet + @throws PropertyVetoException + See com.sun.star.beans.XFastPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XFastPropertySet + */ + public void setFastPropertyValue(int handle, Object value) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + setProperty(translateHandle(handle), value, false, false, (short) 1); + } + + /** + Implements + <code>com.sun.star.beans.XFastPropertySet.getFastPropertyValue</code>. + + @param handle + See com.sun.star.beans.XFastPropertySet + @throws UnknownPropertyException + See com.sun.star.beans.XFastPropertySet + @throws WrappedTargetException + See com.sun.star.beans.XFastPropertySet + @return + See com.sun.star.beans.XFastPropertySet + */ + public Object getFastPropertyValue(int handle) + throws UnknownPropertyException, WrappedTargetException + { + return getProperty(translateHandle(handle), null); + } + + /** + Implements + <code>com.sun.star.beans.XPropertyAccess.getPropertyValues</code>. + + @return + See com.sun.star.beans.XPropertyAccess + */ + public PropertyValue[] getPropertyValues() { + PropertyValue[] s = new PropertyValue[handleMap.length]; + int n = 0; + for (int i = 0; i < handleMap.length; ++i) { + PropertyState[] state = new PropertyState[1]; + Object value; + try { + value = getProperty(handleMap[i], state); + } catch (UnknownPropertyException e) { + continue; + } catch (WrappedTargetException e) { + throw new WrappedTargetRuntimeException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + s[n++] = new PropertyValue(handleMap[i], i, value, state[0]); + } + if (n < handleMap.length) { + PropertyValue[] s2 = new PropertyValue[n]; + System.arraycopy(s, 0, s2, 0, n); + s = s2; + } + return s; + } + + /** + Implements + <code>com.sun.star.beans.XPropertyAccess.setPropertyValues</code>. + + @param props + See com.sun.star.beans.XPropertyAccess + @throws UnknownPropertyException + See com.sun.star.beans.XPropertyAccess + @throws PropertyVetoException + See com.sun.star.beans.XPropertyAccess + @throws WrappedTargetException + See com.sun.star.beans.XPropertyAccess + */ + public void setPropertyValues(PropertyValue[] props) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + for (int i = 0; i < props.length; ++i) { + if (props[i].Handle != -1 + && !props[i].Name.equals(translateHandle(props[i].Handle))) + { + throw new UnknownPropertyException( + ("name " + props[i].Name + " does not match handle " + + props[i].Handle), + object); + } + setProperty( + props[i].Name, props[i].Value, + props[i].State == PropertyState.AMBIGUOUS_VALUE, + props[i].State == PropertyState.DEFAULT_VALUE, (short) 0); + } + } + + /** + A class used by clients of {@link PropertySetMixin} when implementing UNO + interface type attribute setter functions. + + @see #prepareSet(String, Object, Object, PropertySetMixin.BoundListeners) + */ + public static final class BoundListeners { + + /** + Notifies any + <code>com.sun.star.beans.XPropertyChangeListener</code>s. + + @see #prepareSet(String, Object, Object, + PropertySetMixin.BoundListeners) + */ + public void notifyListeners() { + if (specificListeners != null) { + for (Iterator<XPropertyChangeListener> i = specificListeners.iterator(); i.hasNext();) { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + if (unspecificListeners != null) { + for (Iterator<XPropertyChangeListener> i = unspecificListeners.iterator(); i.hasNext();) + { + try { + i.next().propertyChange(event); + } catch (DisposedException e) {} + } + } + } + + private ArrayList<XPropertyChangeListener> specificListeners = null; + private ArrayList<XPropertyChangeListener> unspecificListeners = null; + private PropertyChangeEvent event = null; + } + + private XIdlClass getReflection(String typeName) { + return theCoreReflection.get(context).forName(typeName); + } + + private void initProperties( + XTypeDescription type, HashMap<String,PropertyData> map, ArrayList<String> handleNames, HashSet<String> seen) + { + XInterfaceTypeDescription2 ifc = UnoRuntime.queryInterface( + XInterfaceTypeDescription2.class, resolveTypedefs(type)); + if (!seen.add(ifc.getName())) { + return; + } + XTypeDescription[] bases = ifc.getBaseTypes(); + for (int i = 0; i < bases.length; ++i) { + initProperties(bases[i], map, handleNames, seen); + } + XInterfaceMemberTypeDescription[] members = ifc.getMembers(); + for (int i = 0; i < members.length; ++i) { + if (members[i].getTypeClass() == TypeClass.INTERFACE_ATTRIBUTE) + { + XInterfaceAttributeTypeDescription2 attr = + UnoRuntime.queryInterface( + XInterfaceAttributeTypeDescription2.class, + members[i]); + short attrAttribs = 0; + if (attr.isBound()) { + attrAttribs |= PropertyAttribute.BOUND; + } + boolean setUnknown = false; + if (attr.isReadOnly()) { + attrAttribs |= PropertyAttribute.READONLY; + setUnknown = true; + } + XCompoundTypeDescription[] excs = attr.getGetExceptions(); + boolean getUnknown = false; + //XXX Special interpretation of getter/setter exceptions + // only works if the specified exceptions are of the exact + // type, not of a supertype: + for (int j = 0; j < excs.length; ++j) { + if (excs[j].getName().equals( + "com.sun.star.beans.UnknownPropertyException")) + { + getUnknown = true; + break; + } + } + excs = attr.getSetExceptions(); + for (int j = 0; j < excs.length; ++j) { + if (excs[j].getName().equals( + "com.sun.star.beans.UnknownPropertyException")) + { + setUnknown = true; + } else if (excs[j].getName().equals( + "com.sun.star.beans." + + "PropertyVetoException")) + { + attrAttribs |= PropertyAttribute.CONSTRAINED; + } + } + if (getUnknown && setUnknown) { + attrAttribs |= PropertyAttribute.OPTIONAL; + } + XTypeDescription t = attr.getType(); + for (;;) { + t = resolveTypedefs(t); + short n; + if (t.getName().startsWith( + "com.sun.star.beans.Ambiguous<")) + { + n = PropertyAttribute.MAYBEAMBIGUOUS; + } else if (t.getName().startsWith( + "com.sun.star.beans.Defaulted<")) + { + n = PropertyAttribute.MAYBEDEFAULT; + } else if (t.getName().startsWith( + "com.sun.star.beans.Optional<")) + { + n = PropertyAttribute.MAYBEVOID; + } else { + break; + } + attrAttribs |= n; + t = UnoRuntime.queryInterface(XStructTypeDescription.class, t).getTypeArguments()[0]; + } + String name = members[i].getMemberName(); + boolean present = true; + if (absentOptional != null) { + for (int j = 0; j < absentOptional.length; ++j) { + if (name.equals(absentOptional[j])) { + present = false; + break; + } + } + } + if (map.put( + name, + new PropertyData( + new Property( + name, handleNames.size(), + new Type(t.getName(), t.getTypeClass()), + attrAttribs), + present)) + != null) + { + throw new RuntimeException( + "inconsistent UNO type registry"); + } + handleNames.add(name); + } + } + } + + private String translateHandle(int handle) throws UnknownPropertyException { + if (handle < 0 || handle >= handleMap.length) { + throw new UnknownPropertyException("bad handle " + handle, object); + } + return handleMap[handle]; + } + + private void setProperty( + String name, Object value, boolean isAmbiguous, boolean isDefaulted, + short illegalArgumentPosition) + throws UnknownPropertyException, PropertyVetoException, + com.sun.star.lang.IllegalArgumentException, WrappedTargetException + { + PropertyData p = properties.get(name); + if (p == null) { + throw new UnknownPropertyException(name, object); + } + if ((isAmbiguous + && (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) == 0) + || (isDefaulted + && ((p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) + == 0))) + { + throw new com.sun.star.lang.IllegalArgumentException( + ("flagging as ambiguous/defaulted non-ambiguous/defaulted" + + " property " + name), + object, illegalArgumentPosition); + + } + XIdlField2 f = UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)); + Object[] o = new Object[] { + new Any(type, UnoRuntime.queryInterface(type, object)) }; + Object v = wrapValue( + value, + UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)).getType(), + (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0, + isAmbiguous, + (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0, + isDefaulted, + (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0); + try { + f.set(o, v); + } catch (com.sun.star.lang.IllegalArgumentException e) { + if (e.ArgumentPosition == 1) { + throw new com.sun.star.lang.IllegalArgumentException(e, + e.getMessage(), object, illegalArgumentPosition); + } else { + throw new RuntimeException(e); + } + } catch (com.sun.star.lang.IllegalAccessException e) { + //TODO Clarify whether PropertyVetoException is the correct + // exception to throw when trying to set a read-only property: + throw new PropertyVetoException(e, + "cannot set read-only property " + name, object); + } catch (WrappedTargetRuntimeException e) { + //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not + // guaranteed to originate directly within XIdlField2.get (and thus + // have the expected semantics); it might also be passed through + // from lower layers. + if (new Type(UnknownPropertyException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) + { + throw new UnknownPropertyException(e, name, object); + } else if (new Type(PropertyVetoException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && ((p.property.Attributes + & PropertyAttribute.CONSTRAINED) + != 0)) + { + throw new PropertyVetoException(e, name, object); + } else { + throw new WrappedTargetException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + } + } + + Object getProperty(String name, PropertyState[] state) + throws UnknownPropertyException, WrappedTargetException + { + PropertyData p = properties.get(name); + if (p == null) { + throw new UnknownPropertyException(name, object); + } + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, idlClass.getField(name)); + Object value; + try { + value = field.get( + new Any(type, UnoRuntime.queryInterface(type, object))); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (WrappedTargetRuntimeException e) { + //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not + // guaranteed to originate directly within XIdlField2.get (and thus + // have the expected semantics); it might also be passed through + // from lower layers. + if (new Type(UnknownPropertyException.class).isSupertypeOf( + AnyConverter.getType(e.TargetException)) + && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) + { + throw new UnknownPropertyException(e, name, object); + } else { + throw new WrappedTargetException(e.getCause(), + e.getMessage(), object, e.TargetException); + } + } + boolean undoAmbiguous + = (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0; + boolean undoDefaulted + = (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0; + boolean undoOptional + = (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0; + boolean isAmbiguous = false; + boolean isDefaulted = false; + while (undoAmbiguous || undoDefaulted || undoOptional) { + String typeName = AnyConverter.getType(value).getTypeName(); + if (undoAmbiguous + && typeName.startsWith("com.sun.star.beans.Ambiguous<")) + { + XIdlClass ambiguous = getReflection(typeName); + try { + isAmbiguous = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + ambiguous.getField("IsAmbiguous")).get(value)); + value = UnoRuntime.queryInterface( + XIdlField2.class, + ambiguous.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoAmbiguous = false; + } else if (undoDefaulted + && typeName.startsWith("com.sun.star.beans.Defaulted<")) + { + XIdlClass defaulted = getReflection(typeName); + try { + isDefaulted = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + defaulted.getField("IsDefaulted")).get(value)); + value = UnoRuntime.queryInterface( + XIdlField2.class, + defaulted.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoDefaulted = false; + } else if (undoOptional + && typeName.startsWith("com.sun.star.beans.Optional<")) + { + XIdlClass optional = getReflection(typeName); + try { + boolean present = AnyConverter.toBoolean( + UnoRuntime.queryInterface( + XIdlField2.class, + optional.getField("IsPresent")).get(value)); + if (!present) { + value = Any.VOID; + break; + } + value = UnoRuntime.queryInterface( + XIdlField2.class, + optional.getField("Value")).get(value); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } + undoOptional = false; + } else { + throw new RuntimeException( + "unexpected type of attribute " + name); + } + } + if (state != null) { + //XXX If isAmbiguous && isDefaulted, arbitrarily choose + // AMBIGUOUS_VALUE over DEFAULT_VALUE: + state[0] = isAmbiguous + ? PropertyState.AMBIGUOUS_VALUE + : isDefaulted + ? PropertyState.DEFAULT_VALUE : PropertyState.DIRECT_VALUE; + } + return value; + } + + private Object wrapValue( + Object value, XIdlClass type, boolean wrapAmbiguous, + boolean isAmbiguous, boolean wrapDefaulted, boolean isDefaulted, + boolean wrapOptional) + { + if (wrapAmbiguous + && type.getName().startsWith("com.sun.star.beans.Ambiguous<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + try { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), false, false, wrapDefaulted, + isDefaulted, wrapOptional)); + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsAmbiguous")).set( + strct, Boolean.valueOf(isAmbiguous)); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else if (wrapDefaulted + && type.getName().startsWith( + "com.sun.star.beans.Defaulted<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + try { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), wrapAmbiguous, isAmbiguous, + false, false, wrapOptional)); + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsDefaulted")).set( + strct, Boolean.valueOf(isDefaulted)); + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else if (wrapOptional + && type.getName().startsWith("com.sun.star.beans.Optional<")) + { + Object[] strct = new Object[1]; + type.createObject(strct); + boolean present = !AnyConverter.isVoid(value); + try { + UnoRuntime.queryInterface( + XIdlField2.class, type.getField("IsPresent")).set( + strct, Boolean.valueOf(present)); + if (present) { + XIdlField2 field = UnoRuntime.queryInterface( + XIdlField2.class, type.getField("Value")); + field.set( + strct, + wrapValue( + value, field.getType(), wrapAmbiguous, isAmbiguous, + wrapDefaulted, isDefaulted, false)); + } + } catch (com.sun.star.lang.IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (com.sun.star.lang.IllegalAccessException e) { + throw new RuntimeException(e); + } + return strct[0]; + } else { + if (wrapAmbiguous || wrapDefaulted || wrapOptional) { + throw new RuntimeException("unexpected type of attribute"); + } + return value; + } + } + + private static XTypeDescription resolveTypedefs(XTypeDescription type) { + while (type.getTypeClass() == TypeClass.TYPEDEF) { + type = UnoRuntime.queryInterface( + XIndirectTypeDescription.class, type).getReferencedType(); + } + return type; + } + + private PropertyData get(Object object, String propertyName) + throws UnknownPropertyException + { + PropertyData p = properties.get(propertyName); + if (p == null || !p.present) { + throw new UnknownPropertyException(propertyName, object); + } + return p; + } + + private void checkUnknown(String propertyName) + throws UnknownPropertyException + { + if (propertyName.length() != 0) { + get(this, propertyName); + } + } + + private static final class PropertyData { + public PropertyData(Property property, boolean present) { + this.property = property; + this.present = present; + } + + public final Property property; + public final boolean present; + } + + private final class Info extends WeakBase implements XPropertySetInfo + { + public Info(Map<String,PropertyData> properties) { + this.properties = properties; + } + + public Property[] getProperties() { + ArrayList<Property> al = new ArrayList<Property>(properties.size()); + for (Iterator<PropertyData> i = properties.values().iterator(); i.hasNext();) { + PropertyData p = i.next(); + if (p.present) { + al.add(p.property); + } + } + return al.toArray(new Property[al.size()]); + } + + public Property getPropertyByName(String name) + throws UnknownPropertyException + { + return get(this, name).property; + } + + public boolean hasPropertyByName(String name) { + PropertyData p = properties.get(name); + return p != null && p.present; + } + + private final Map<String,PropertyData> properties; + } + + private final XComponentContext context; + private final XInterface object; + private final Type type; + private final String[] absentOptional; + private final XIdlClass idlClass; + private final Map<String,PropertyData> properties; // from String to Property + private final String[] handleMap; + + private HashMap<String,ArrayList<XPropertyChangeListener>> boundListeners + = new HashMap<String,ArrayList<XPropertyChangeListener>>(); + // from String to Vector of XPropertyChangeListener + private HashMap<String,ArrayList<XVetoableChangeListener>> vetoListeners + = new HashMap<String,ArrayList<XVetoableChangeListener>>(); + // from String to Vector of XVetoableChangeListener + private boolean disposed = false; +} diff --git a/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java new file mode 100644 index 000000000..8bb4d2643 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/UnoUrl.java @@ -0,0 +1,401 @@ +/* + * 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.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.HashMap; + +/** + * Object representation and parsing of Uno Urls, + * which allow to locate a named Uno object in a + * different process. A Uno Url consists of the + * specification of a connection, protocol and + * rootOid delimited with a ';'. + * The syntax of a Uno Url is + * + * <code> + * [uno:]connection-type,parameters;protocol-name,parameters;objectname"; + * </code> + * + * An example Uno Url will look like this: + * + * <code> + * socket,host=localhost,port=2002;urp;StarOffice.ServiceManager + * </code> + * + * For more information about Uno Url please consult + * <a href="http://udk.openoffice.org/common/man/spec/uno-url.html"> + * http://udk.openoffice.org/common/man/spec/uno-url.html</a> + * + * Usage: + * + * <code> + * UnoUrl url = UnoUrl.parseUnoUrl("socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"); + * </code> + */ +public class UnoUrl { + + private static final String FORMAT_ERROR = + "syntax: [uno:]connection-type,parameters;protocol-name,parameters;objectname"; + + private static final String VALUE_CHAR_SET = "!$&'()*+-./:?@_~"; + private static final String OID_CHAR_SET = VALUE_CHAR_SET + ",="; + + private final UnoUrlPart connection; + private final UnoUrlPart protocol; + private final String rootOid; + + private static class UnoUrlPart { + + private final String partTypeName; + private final HashMap<String,String> partParameters; + private final String uninterpretedParameterString; + + public UnoUrlPart( + String uninterpretedParameterString, + String partTypeName, + HashMap<String,String> partParameters) { + this.uninterpretedParameterString = uninterpretedParameterString; + this.partTypeName = partTypeName; + this.partParameters = partParameters; + } + + public String getPartTypeName() { + return partTypeName; + } + + public HashMap<String,String> getPartParameters() { + return partParameters; + } + + public String getUninterpretedParameterString() { + return uninterpretedParameterString; + } + + public String getUninterpretedString() { + StringBuffer buf = new StringBuffer(partTypeName); + if (uninterpretedParameterString.length() > 0) { + buf.append(','); + buf.append(uninterpretedParameterString); + } + return buf.toString(); + } + } + + private UnoUrl( + UnoUrlPart connectionPart, + UnoUrlPart protocolPart, + String rootOid) { + this.connection = connectionPart; + this.protocol = protocolPart; + this.rootOid = rootOid; + } + + /** + * Returns the name of the connection of this + * Uno Url. Encoded characters are not allowed. + * + * @return The connection name as string. + */ + public String getConnection() { + return connection.getPartTypeName(); + } + + /** + * Returns the name of the protocol of this + * Uno Url. Encoded characters are not allowed. + * + * @return The protocol name as string. + */ + public String getProtocol() { + return protocol.getPartTypeName(); + } + + /** + * Return the object name. Encoded character are + * not allowed. + * + * @return The object name as String. + */ + public String getRootOid() { + return rootOid; + } + + /** + * Returns the protocol parameters as + * a Hashmap with key/value pairs. Encoded + * characters like '%41' are decoded. + * + * @return a HashMap with key/value pairs for protocol parameters. + */ + public HashMap<String,String> getProtocolParameters() { + return protocol.getPartParameters(); + } + + /** + * Returns the connection parameters as + * a Hashmap with key/value pairs. Encoded + * characters like '%41' are decoded. + * + * @return a HashMap with key/value pairs for connection parameters. + */ + public HashMap<String,String> getConnectionParameters() { + return connection.getPartParameters(); + } + + /** + * Returns the raw specification of the protocol + * parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted protocol parameters as string. + */ + public String getProtocolParametersAsString() { + return protocol.getUninterpretedParameterString(); + } + + /** + * Returns the raw specification of the connection + * parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted connection parameters as string. + */ + public String getConnectionParametersAsString() { + return connection.getUninterpretedParameterString(); + } + + /** + * Returns the raw specification of the protocol + * name and parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted protocol name and parameters as string. + */ + public String getProtocolAndParametersAsString() { + return protocol.getUninterpretedString(); + } + + /** + * Returns the raw specification of the connection + * name and parameters. Encoded characters like '%41' are + * not decoded. + * + * @return The uninterpreted connection name and parameters as string. + */ + public String getConnectionAndParametersAsString() { + return connection.getUninterpretedString(); + } + + private static String decodeUTF8(String s) + throws com.sun.star.lang.IllegalArgumentException { + + if (!s.contains("%")) { + return s; + } + try { + int length = s.length(); + ByteBuffer bb = ByteBuffer.allocate(length); + for (int i = 0; i < length; i++) { + int ch = s.charAt(i); + + if (ch == '%') { + if (i+3 > length) + throw new com.sun.star.lang.IllegalArgumentException( + "Incomplete trailing escape (%) pattern"); + try { + ch = Integer.parseInt(s.substring(i+1,i+3),16); + } catch (NumberFormatException e) { + throw new com.sun.star.lang.IllegalArgumentException(e); + } + if (ch < 0) + throw new com.sun.star.lang.IllegalArgumentException( + "Illegal hex characters in escape (%) pattern - negative value"); + i+=2; + } + + bb.put((byte) (ch & 0xFF)); + } + + byte[] bytes = new byte[bb.position()]; + System.arraycopy(bb.array(), 0, bytes, 0, bytes.length); + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new com.sun.star.lang.IllegalArgumentException(e, + "Couldn't convert parameter string to UTF-8 string"); + } + } + + private static HashMap<String,String> buildParamHashMap(String paramString) + throws com.sun.star.lang.IllegalArgumentException { + HashMap<String,String> params = new HashMap<String,String>(); + + int pos = 0; + + while (true) { + char c = ','; + + StringBuffer sb = new StringBuffer(); + while ((pos < paramString.length()) + && ((c = paramString.charAt(pos++)) != '=')) { + sb.append(c); + } + String aKey = sb.toString(); + + sb = new StringBuffer(); + while ((pos < paramString.length()) + && ((c = paramString.charAt(pos++)) != ',') + && c != ';') { + sb.append(c); + } + String aValue = sb.toString(); + + if ((aKey.length() > 0) && (aValue.length() > 0)) { + + if (!isAlphaNumeric(aKey)) { + throw new com.sun.star.lang.IllegalArgumentException( + "The parameter key '" + + aKey + + "' may only consist of alpha numeric ASCII characters."); + } + + if (!isValidString(aValue, VALUE_CHAR_SET + "%")) { + throw new com.sun.star.lang.IllegalArgumentException( + "The parameter value for key '" + aKey + "' contains illegal characters."); + } + + params.put(aKey, decodeUTF8(aValue)); + } + + if ((pos >= paramString.length()) || (c != ',')) + break; + + } + + return params; + } + + private static UnoUrlPart parseUnoUrlPart(String thePart) + throws com.sun.star.lang.IllegalArgumentException { + String partName; + String theParamPart; + int index = thePart.indexOf(','); + if (index != -1) { + partName = thePart.substring(0, index).trim(); + theParamPart = thePart.substring(index + 1).trim(); + } else { + partName = thePart; + theParamPart = ""; + } + + if (!isAlphaNumeric(partName)) { + throw new com.sun.star.lang.IllegalArgumentException( + "The part name '" + + partName + + "' may only consist of alpha numeric ASCII characters."); + } + + HashMap<String,String> params = buildParamHashMap(theParamPart); + + return new UnoUrlPart(theParamPart, partName, params); + } + + private static boolean isAlphaNumeric(String s) { + return isValidString(s, null); + } + + private static boolean isValidString(String identifier, String validCharSet) { + + int len = identifier.length(); + + for (int i = 0; i < len; i++) { + + int ch = identifier.charAt(i); + + boolean isValidChar = + ('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9'); + + if (!isValidChar && (validCharSet != null)) { + isValidChar = (validCharSet.indexOf(ch) != -1); + } + + if (!isValidChar) + return false; + } + + return true; + } + + /** + * Parses the given Uno Url and returns + * an in memory object representation. + * + * @param unoUrl The given uno URl as string. + * @return Object representation of class UnoUrl. + * @throws IllegalArgumentException if Url cannot be parsed. + */ + public static UnoUrl parseUnoUrl(String unoUrl) + throws com.sun.star.lang.IllegalArgumentException { + + String url = unoUrl; + + int index = url.indexOf(':'); + if (index != -1) { + String unoStr = url.substring(0, index).trim(); + if (!"uno".equals(unoStr)) { + throw new com.sun.star.lang.IllegalArgumentException( + "Uno Urls must start with 'uno:'. " + FORMAT_ERROR); + } + } + + url = url.substring(index + 1).trim(); + + index = url.indexOf(';'); + if (index == -1) { + throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR); + } + + String connection = url.substring(0, index).trim(); + url = url.substring(index + 1).trim(); + + UnoUrlPart connectionPart = parseUnoUrlPart(connection); + + index = url.indexOf(';'); + if (index == -1) { + throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR); + } + + String protocol = url.substring(0, index).trim(); + url = url.substring(index + 1).trim(); + + UnoUrlPart protocolPart = parseUnoUrlPart(protocol); + + String rootOid = url.trim(); + if (!isValidString(rootOid, OID_CHAR_SET)) { + throw new com.sun.star.lang.IllegalArgumentException( + "Root OID '"+ rootOid + "' contains illegal characters."); + } + + return new UnoUrl(connectionPart, protocolPart, rootOid); + + } + +} diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java new file mode 100644 index 000000000..67e01ac32 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/WeakAdapter.java @@ -0,0 +1,94 @@ +/* + * 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.lang.ref.WeakReference; +import com.sun.star.uno.XAdapter; +import com.sun.star.uno.XReference; +import java.util.List; +import java.util.Collections; +import java.util.LinkedList; + +/** An XAdapter implementation that holds a weak reference (java.lang.ref.WeakReference) + * to an object. Clients can register listener (com.sun.star.lang.XReference) which + * are notified when the object (the one which is kept weak) is being finalized. That + * is, that object is being destroyed because there are not any hard references + * to it. + */ +public class WeakAdapter implements XAdapter +{ + // references the XWeak implementation + private final WeakReference<Object> m_weakRef; + // contains XReference objects registered by addReference + private final List<XReference> m_xreferenceList; + + /** + *@param component the object that is to be held weak + */ + public WeakAdapter(Object component) + { + m_weakRef= new WeakReference<Object>(component); + m_xreferenceList= Collections.synchronizedList( new LinkedList<XReference>()); + } + + /** Called by the XWeak implementation (WeakBase) when it is being finalized. + * It is only being called once. + * The registered XReference listeners are notified. On notification they are + * to unregister themselves. The notification is thread-safe. However, it is possible + * to add a listener during the notification process, which will never receive a + * notification. To prevent this, one would have to synchronize this method with + * the addReference method. But this can result in deadlocks in a multi-threaded + * environment. + */ + void referentDying() + { + //synchronized call + XReference[] references= m_xreferenceList.toArray(new XReference[m_xreferenceList.size()]); + for (int i= references.length; i > 0; i--) + { + references[i-1].dispose(); + } + } + + /** Method of com.sun.star.uno.XAdapter. It is called to obtain a hard reference + * to the object which is kept weak by this instance. + * @return hard reference to the object + */ + public Object queryAdapted() + { + return m_weakRef.get(); + } + + /** Method of com.sun.star.uno.XAdapter. Called by clients to register listener which + * are notified when the weak object is dying. + *@param xReference a listener + */ + public void removeReference(XReference xReference) + { + m_xreferenceList.remove(xReference); + } + + /** Method of com.sun.star.uno.XAdapter. Called by clients to unregister listeners. + *@param xReference listener + */ + public void addReference(XReference xReference) + { + m_xreferenceList.add(xReference); + } +} + diff --git a/ridljar/com/sun/star/lib/uno/helper/WeakBase.java b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java new file mode 100644 index 000000000..ac175d3a6 --- /dev/null +++ b/ridljar/com/sun/star/lib/uno/helper/WeakBase.java @@ -0,0 +1,101 @@ +/* + * 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 com.sun.star.uno.XWeak; +import com.sun.star.uno.XAdapter; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + + +/** This class can be used as the base class for UNO components. It implements the capability + * to be kept weak (com.sun.star.uno.XWeak) and it implements com.sun.star.lang.XTypeProvider + * which is necessary for using the component with StarBasic. + */ +public class WeakBase implements XWeak, XTypeProvider +{ + // Contains all WeakAdapter which have been created in this class + // They have to be notified when this object dies + private WeakAdapter m_adapter; + + protected static Map<Class<?>,Type[]> _mapTypes = new HashMap<Class<?>,Type[]>(); + + /** Method of XWeak. The returned XAdapter implementation can be used to keep + * a weak reference to this object. + * @return a com.sun.star.uno.XAdapter implementation. + */ + synchronized public XAdapter queryAdapter() + { + if (m_adapter == null) + m_adapter= new WeakAdapter(this); + return m_adapter; + } + + /** Override of Object.finalize. When there are no references to this object anymore + * then the garbage collector calls this method. Thereby causing the adapter object + * to be notified. The adapter, in turn, notifies all listeners (com.sun.star.uno.XReference) + */ + @Override + protected void finalize() throws java.lang.Throwable + { + if (m_adapter != null) + m_adapter.referentDying(); + super.finalize(); + } + + /** Method of XTypeProvider. It returns an array of Type objects which represent + * all implemented UNO interfaces of this object. + * @return Type objects of all implemented interfaces. + */ + public Type[] getTypes() + { + Type[] arTypes= _mapTypes.get( getClass()); + if (arTypes == null) + { + ArrayList<Type> vec= new ArrayList<Type>(); + Class currentClass= getClass(); + do + { + Class interfaces[]= currentClass.getInterfaces(); + for(int i = 0; i < interfaces.length; ++ i) + { + // Test if it is a UNO interface + if (com.sun.star.uno.XInterface.class.isAssignableFrom(interfaces[i])) + vec.add(new Type(interfaces[i])); + } + // get the superclass the currentClass inherits from + currentClass= currentClass.getSuperclass(); + } while (currentClass != null); + + Type types[]= vec.toArray(new Type[vec.size()]); + _mapTypes.put(getClass(), types); + arTypes= types; + } + return arTypes; + } + + /** Obsolete method of XTypeProvider. + */ + public byte[] getImplementationId() + { + return new byte[0]; + } +} |