1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 package com.sun.star.lib.uno.helper;
28 
29 import com.sun.star.uno.Type;
30 import com.sun.star.lang.EventObject;
31 import com.sun.star.lang.WrappedTargetException;
32 import com.sun.star.uno.TypeClass;
33 import com.sun.star.uno.AnyConverter;
34 import com.sun.star.uno.XInterface;
35 import com.sun.star.uno.Any;
36 import com.sun.star.uno.UnoRuntime;
37 import com.sun.star.beans.XPropertyChangeListener;
38 import com.sun.star.beans.XVetoableChangeListener;
39 import com.sun.star.beans.PropertyChangeEvent;
40 import com.sun.star.beans.XPropertySet;
41 import com.sun.star.beans.Property;
42 import com.sun.star.beans.PropertyAttribute;
43 import com.sun.star.beans.UnknownPropertyException;
44 import com.sun.star.beans.XPropertiesChangeListener;
45 import com.sun.star.beans.XPropertySetInfo;
46 import com.sun.star.beans.XFastPropertySet;
47 import com.sun.star.beans.PropertyVetoException;
48 import com.sun.star.beans.XMultiPropertySet;
49 import java.util.Iterator;
50 import java.util.Collection;
51 import java.util.HashMap;
52 import java.lang.reflect.Field;
53 import com.sun.star.lang.DisposedException;
54 
55 
56 /** This class is an implementation of the interfaces com.sun.star.beans.XPropertySet,
57  *  com.sun.star.beans.XFastPropertySet and com.sun.star.beans.XMultiPropertySet. This
58  *  class has to be inherited to be used. The values of properties are stored in member
59  *  variables of the inheriting class. By overriding the methods
60  *  {@link #convertPropertyValue convertPropertyValue},
61  *  {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and
62  *  {@link #getPropertyValue(Property)} one can determine how
63  *  property values are stored.
64  *  When using the supplied implementations of this class then the member variables which
65  *  hold property values have to be declared in the class which inherits last in the inheriting
66  *  chain and they have to be public<p>
67  *  Properties have to be registered by one of the registerProperty methods. They take among other
68  *  arguments an Object named <em>id</em> which has to be a String that represents the name of
69  *  the member variable. The registering has to occur in the constructor of the inheriting class.
70  *  It is no allowed to add or change properties later on.<p>
71  *  Example:
72  *  <pre>
73  *  public class Foo extends PropertySet
74  *  {
75  *      protected int intProp;
76  *
77  *      public Foo()
78  *      {
79  *          registerProperty("PropertyA", 0, new Type(int.class), (short)0, "intProp");
80  *      }
81  *  }
82  *
83  *  </pre>
84  */
85 public class PropertySet extends ComponentBase implements XPropertySet, XFastPropertySet,
86 XMultiPropertySet
87 {
88     private HashMap _nameToPropertyMap;
89     private HashMap _handleToPropertyMap;
90     private HashMap _propertyToIdMap;
91     private Property[] arProperties;
92 
93     private int lastHandle= 1;
94 
95     protected XPropertySetInfo propertySetInfo;
96     protected MultiTypeInterfaceContainer aBoundLC= new MultiTypeInterfaceContainer();
97     protected MultiTypeInterfaceContainer aVetoableLC= new MultiTypeInterfaceContainer();
98     public PropertySet()
99     {
100         super();
101         initMappings();
102     }
103 
104     /** Registers a property with this helper class and associates the argument <em>id</em> with it.
105      *  <em>id</em> is used to identify the storage of the property value. How property values are stored
106      *  and retrieved is determined by the methods {@link #convertPropertyValue convertPropertyValue},
107      *  {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and {@link #getPropertyValue(Property) getPropertyValue}
108      *  These methods expect <em>id</em> to be a java.lang.String which represents the name of a member variable
109      *  which holds the property value.
110      *  Only properties which are registered can be accessed. Registration has to occur during
111      *  initialization of the inheriting class (i.e. within the contructor).
112      *  @param prop The property to be registered.
113      *  @param id Identifies the properties storage.
114      *  @see #getPropertyId
115      */
116     protected void registerProperty(Property prop, Object id)
117     {
118         putProperty(prop);
119         assignPropertyId(prop, id);
120     }
121 
122     /** Registers a property with this helper class and associates the argument id with it.
123      *  It does the same as {@link #registerProperty(Property, Object)}. The first four
124      *  arguments are used to construct a Property object.
125      *  Registration has to occur during
126      *  initialization of the inheriting class (i.e. within the contructor)
127      *  @param name The property's name (Property.Name).
128      *  @param handle The property's handle (Property.Handle).
129      *  @param Type The property's type (Property.Type).
130      *  @param attributes The property's attributes (Property.Attributes).
131      *  @param id Identifies the property's storage.
132      */
133     protected void registerProperty(String name, int handle, Type type, short attributes, Object id)
134     {
135         Property p= new Property(name, handle, type, attributes);
136         registerProperty(p, id);
137     }
138 
139     /** Registers a property with this  class and associates the argument id with it.
140      *  It does the same as {@link #registerProperty(Property, Object)}. The first three
141      *  arguments are used to construct a Property object. The value for the Property.Handle
142      *  is generated and does not have to be specified here. Use this method for registering
143      *  a property if you do not care about the Property's handles.
144      *  Registration has to occur during
145      *  initialization of the inheriting class (i.e. within the contructor).
146      *  @param name The property's name (Property.Name).
147      *  @param handle The property's handle (Property.Handle).
148      *  @param Type The property's type (Property.Type).
149      *  @param attributes The property's attributes (Property.Attributes).
150      *  @param id Identifies the property's storage.
151      */
152     protected void registerProperty(String name, Type type, short attributes, Object id)
153     {
154         Property p= new Property(name, lastHandle++, type, attributes);
155         registerProperty(p, id);
156     }
157 
158     /** Registers a property with this class. This method expects that property values
159      *  are stored in member variables as is the case if the methods convertPropertyValue,
160      *  setPropertyValueNoBroadcast and getPropertyValue(Property) are not overridden.
161      *  It is presumed that the type of the member variable
162      *  corresponds Property.Type. For example, if the TypeClass of Property.Type is to be
163      *  a TypeClass.SHORT then the member must be a short or java.lang.Short.
164      *  The handle for the property is generated.<br>
165      *  If there is no member with the specified name or if the member has an incompatible type
166      *  then a com.sun.star.uno.RuntimeException is thrown.
167      *  @param propertyName The name of the property.
168      *  @param memberName The name of the member variable that holds the value of the property.
169      *  @param attributes The property attributes.
170      */
171     protected void registerProperty(String propertyName, String memberName, short attributes)
172     {
173         Field propField= null;
174         try
175         {
176             propField= getClass().getDeclaredField(memberName);
177         }
178         catch (NoSuchFieldException e)
179         {
180             throw new com.sun.star.uno.RuntimeException("there is no member variable: " + memberName);
181         }
182         Class cl= propField.getType();
183         Type t= new Type(cl);
184         if (t.getTypeClass() != TypeClass.UNKNOWN)
185         {
186             Property p= new Property(propertyName, lastHandle++,  t, attributes);
187             registerProperty(p,memberName);
188         }
189         else
190             throw new com.sun.star.uno.RuntimeException("the member has an unknown type: " + memberName);
191     }
192 
193     /** Registers a property with this class.
194      *  It is presumed that the name of property is equal to the name of the member variable
195      *  that holds the property value.
196      *  @param propertyName The name of the property and the member variable that holds the property's value.
197      *  @param attributes The property attributes.
198      *  @see #registerProperty(String, String, short)
199      */
200     protected void registerProperty(String propertyName, short attributes)
201     {
202         registerProperty(propertyName, propertyName, attributes);
203     }
204 
205 
206 
207     /** Returns the Property object for a given property name or null if that property does
208      *  not exists (i.e. it has not been registered). Override this method
209      *  if you want to implement your own mapping from property names to Property objects.
210      *  Then you also have to override {@link #initMappings}, {@link #getProperties()} and
211      *  {@link #putProperty(Property)}.
212      *  @param propertyName The name of the property (Property.Name)
213      *  @return The Property object with the name <em>propertyName</em>.
214      */
215     protected Property getProperty(String propertyName)
216     {
217         return (Property) _nameToPropertyMap.get(propertyName);
218     }
219 
220     /** Returns the Property object with a handle (Property.Handle) as specified by the argument
221      *  <em>nHandle</em>. The method returns null if there is no such property (i.e. it has not
222      *  been registered). Override this method if you want to implement your own mapping from handles
223      *  to Property objects. Then you also have to override {@link #initMappings}, {@link #putProperty(Property)}.
224      *  @param nHandle The handle of the property (Property.Handle).
225      *  @return The Property object with the handle <em>nHandle</em>
226      */
227     protected Property getPropertyByHandle(int nHandle)
228     {
229         return (Property) _handleToPropertyMap.get(new Integer(nHandle));
230     }
231 
232     /** Returns an array of all Property objects or an array of length null if there
233      *  are no properties. Override this method if you want to implement your own mapping from names
234      *  to Property objects. Then you also have to override {@link #initMappings}, {@link #getProperty(String)} and
235      *  {@link #putProperty}.
236      *  @return Array of all Property objects.
237      */
238     protected Property[] getProperties()
239     {
240         if (arProperties == null)
241         {
242             Collection values= _nameToPropertyMap.values();
243                 arProperties= (Property[]) values.toArray(new Property[_nameToPropertyMap.size()]);
244         }
245         return arProperties;
246     }
247 
248     /** Stores a Property object so that it can be retrieved subsequently by
249      *  {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}.
250      *  Override this method if you want to implement your own mapping from handles
251      *  to Property objects and names to Property objects. Then you also need to override {@link #initMappings},
252      *  {@link #getProperty(String)},{@link #getProperties()},{@link #getPropertyByHandle(int)}.
253      *  @param prop The Property object that is to be stored.
254      */
255     protected void putProperty(Property prop)
256     {
257         _nameToPropertyMap.put(prop.Name, prop);
258         if (prop.Handle != -1)
259             _handleToPropertyMap.put(new Integer(prop.Handle), prop);
260     }
261 
262     /** Assigns an identifyer object to a Property object so that the identifyer
263      *  can be obtained by {@link #getPropertyId getPropertyId} later on. The identifyer
264      *  is used to specify a certain storage for the property's value. If you do not
265      *  override {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} or {@link #getPropertyValue(Property)}
266      *  then the argument <em>id</em> has to be a java.lang.String that equals the name of
267      *  the member variable that holds the Property's value.
268      *  Override this method if you want to implement your own mapping from Property objects  to ids or
269      *  if you need ids of a type other then java.lang.String.
270      *  Then you also need to override {@link #initMappings initMappings} and {@link #getPropertyId getPropertyId}.
271      *  @param prop The Property object that is being assigned an id.
272      *  @param id The object which identifies the storage used for the property's value.
273      *  @see #registerProperty(Property, Object)
274      */
275     protected void assignPropertyId(Property prop, Object id)
276     {
277        if (id instanceof String && ((String) id).equals("") == false)
278             _propertyToIdMap.put(prop, id);
279     }
280 
281     /** Returns the identifyer object for a certain Property. The object must have been
282      *  previously assigned to the Property object by {@link #assignPropertyId assignPropertyId}.
283      *  Override this method if you want to implement your own mapping from Property objects to ids.
284      *  Then you also need to override {@link #initMappings initMappings} and {@link #assignPropertyId assignPropertyId}.
285      *  @param prop The property for which the id is to be retrieved.
286      *  @return The id object that identifies the storage used for the property's value.
287      *  @see #registerProperty(Property, Object)
288      */
289     protected Object getPropertyId(Property prop)
290     {
291         return _propertyToIdMap.get(prop);
292     }
293 
294     /** Initializes data structures used for mappings of property names to property object,
295      *  property handles to property objects and property objects to id objects.
296      *  Override this method if you want to implement your own mappings. Then you also need to
297      *  override {@link #putProperty putProperty},{@link #getProperty getProperty}, {@link #getPropertyByHandle},
298      *  {@link #assignPropertyId assignPropertyId} and {@link #getPropertyId getPropertyId}.
299      */
300     protected void initMappings()
301     {
302        _nameToPropertyMap= new HashMap();
303        _handleToPropertyMap= new HashMap();
304        _propertyToIdMap= new HashMap();
305     }
306 
307     /** Makes sure that listeners which are kept in aBoundLC (XPropertyChangeListener) and aVetoableLC
308      *  (XVetoableChangeListener) receive a disposing call. Also those listeners are relesased.
309      */
310     protected void postDisposing()
311     {
312         // Create an event with this as sender
313     	EventObject aEvt= new EventObject(this);
314 
315         // inform all listeners to reelease this object
316     	aBoundLC.disposeAndClear(aEvt);
317         aVetoableLC.disposeAndClear(aEvt);
318     }
319 
320     //XPropertySet ----------------------------------------------------
321     synchronized public void addPropertyChangeListener(String str, XPropertyChangeListener xPropertyChangeListener)
322     throws UnknownPropertyException, WrappedTargetException
323     {
324   		// only add listeners if you are not disposed
325         if (! bInDispose && ! bDisposed)
326         {
327             if (str.length() > 0)
328             {
329                 Property prop= getProperty(str);
330                 if (prop == null)
331                     throw new UnknownPropertyException("Property " + str + " is unknown");
332 
333                 // Add listener for a certain property
334                 if ((prop.Attributes & PropertyAttribute.BOUND) > 0)
335                     aBoundLC.addInterface(str, xPropertyChangeListener);
336                 else
337                     //ignore silently
338                     return;
339             }
340             else
341                 // Add listener for all properties
342                 listenerContainer.addInterface(XPropertyChangeListener.class, xPropertyChangeListener);
343         }
344     }
345     //XPropertySet ----------------------------------------------------
346     synchronized public void addVetoableChangeListener(String str, com.sun.star.beans.XVetoableChangeListener xVetoableChangeListener) throws com.sun.star.beans.UnknownPropertyException, com.sun.star.lang.WrappedTargetException
347     {
348  		// only add listeners if you are not disposed
349         if (! bInDispose && ! bDisposed)
350         {
351             if (str.length() > 0)
352             {
353                 Property prop= getProperty(str);
354                 if (prop == null)
355                     throw new UnknownPropertyException("Property " + str + " is unknown");
356 
357                 // Add listener for a certain property
358                 if ((prop.Attributes & PropertyAttribute.CONSTRAINED) > 0)
359                     aVetoableLC.addInterface(str, xVetoableChangeListener);
360                 else
361                     //ignore silently
362                     return;
363             }
364             else
365                 // Add listener for all properties
366                 listenerContainer.addInterface(XVetoableChangeListener.class, xVetoableChangeListener);
367         }
368     }
369     //XPropertySet ----------------------------------------------------
370     public com.sun.star.beans.XPropertySetInfo getPropertySetInfo()
371     {
372         if (propertySetInfo == null)
373         {
374             synchronized (this)
375             {
376                 if (propertySetInfo == null)
377                     propertySetInfo= new PropertySetInfo();
378             }
379         }
380         return propertySetInfo;
381     }
382     //XPropertySet ----------------------------------------------------
383     public Object getPropertyValue(String name) throws UnknownPropertyException, WrappedTargetException
384     {
385         Object ret= null;
386         if (bInDispose || bDisposed)
387             throw new com.sun.star.lang.DisposedException("The component has been disposed already");
388 
389         Property prop= getProperty(name);
390         if (prop == null)
391             throw new UnknownPropertyException("The property " + name + " is unknown");
392 
393         synchronized (this)
394         {
395             ret= getPropertyValue(prop);
396         }
397         // null must not be returned. Either a void any is returned or an any containing
398         // an interface type and a null reference.
399         if (ret == null)
400         {
401             if (prop.Type.getTypeClass() == TypeClass.INTERFACE)
402                 ret= new Any(prop.Type, null);
403             else
404                 ret= new Any(new Type(void.class), null);
405         }
406         return ret;
407     }
408 
409     //XPropertySet ----------------------------------------------------
410     synchronized public void removePropertyChangeListener(String propName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException
411     {	// all listeners are automaticly released in a dispose call
412         if (!bInDispose && !bDisposed)
413         {
414             if (propName.length() > 0)
415             {
416                 Property prop = getProperty(propName);
417                 if (prop == null)
418                     throw new UnknownPropertyException("Property " + propName + " is unknown");
419                 aBoundLC.removeInterface(propName, listener);
420             }
421             else
422                 listenerContainer.removeInterface(XPropertyChangeListener.class, listener);
423         }
424     }
425 
426     //XPropertySet ----------------------------------------------------
427     synchronized public void removeVetoableChangeListener(String propName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException
428     {// all listeners are automaticly released in a dispose call
429         if (!bInDispose && !bDisposed)
430         {
431             if (propName.length() > 0)
432             {
433                 Property prop = getProperty(propName);
434                 if (prop == null)
435                     throw new UnknownPropertyException("Property " + propName + " is unknown");
436                 aVetoableLC.removeInterface(propName, listener);
437             }
438             else
439                 listenerContainer.removeInterface(XVetoableChangeListener.class, listener);
440         }
441     }
442 
443     //XPropertySet ----------------------------------------------------
444     /** Sets the value of a property.
445      *  The idl description for this interfaces, stipulates that the argument value is an Any. Since a java.lang.Object
446      *  reference has the same meaning as an Any this function accepts
447      *  java anys (com.sun.star.uno.Any) and all other appropriate objects as arguments. The value argument can be one
448      *  of these:
449      *  <ul>
450      *  <li>java.lang.Boolean</li>
451      *  <li>java.lang.Character</li>
452      *  <li>java.lang.Byte</li>
453      *  <li>java.lang.Short</li>
454      *  <li>java.lang.Integer</li>
455      *  <li>java.lang.Long</li>
456      *  <li>java.lang.Float</li>
457      *  <li>java.lang.Double</li>
458      *  <li>java.lang.String</li>
459      *  <li>com.sun.star.uno.Type</li>
460      *  <li><em>objects which implement UNO interfaces</em></li>
461      *  <li><em>arrays which contain elements of the types above</em></li>
462      *  <li>com.sun.star.uno.Any containing an instance of one of the above types</li>
463      *  </ul>
464      *
465      *  Properties can have the attribute com.sun.star.beans.PropertyAttribute.MAYBEVOID, which means that the value
466      *  (not the type) can be void. In order to assign a void value to a property one can either pass an Any which
467      *  contains a null reference or pass null directly. In bothe cases the null reference is only accepted if
468      *  the PropertyAttribute.MAYBEVOID attribute is set for the property.
469      *
470      *  Properties which have the attribute MAYBEVOID set (Property.Attributes) can have a void value. The following
471      *  considerations presume that the Property has that attribute set. Further, when mentioning an Any's value we
472      *  actually refer to the object returned by Any.getObject.
473      *  If the argument <em>value</em> is null, or it is an Any whose value is null (but with a valid Type)
474      *  then the member variable used for storing the property's value is set to null.
475      *  Therefore those properties can only be stored in objects
476      *  and primitive types are not allowed (one can use the wrapper classes instead,e.g. java.lang.Byte) .
477      *  If a property's value is kept in a member variable of type Any and that reference is still null
478      *  then when setPropertyValue is called with
479      *  <em>value</em> = null then the member variable is assigned an Any with type void and a null value.
480      *  Or if the argument is an Any with a null value then it is assigned to the member variable.
481      *  Further, if the variable already
482      *  references an Any and setPropertyValue is called with <em>value</em> = null, then the variable is assigned
483      *  a new Any with the same type as the previously referenced Any and with a null value.
484      *  @param name The name of the property.
485      *  @param value The new value of the property.
486      *     *     */
487     public void setPropertyValue(String name, Object value) throws UnknownPropertyException,
488     PropertyVetoException, com.sun.star.lang.IllegalArgumentException,  WrappedTargetException
489     {
490         Property prop= getProperty(name);
491         if (prop == null)
492             throw new UnknownPropertyException("Property " + name + " is unknown");
493         setPropertyValue(prop, value);
494     }
495 
496     /** Sets the value of a property. It checks if the property's attributes (READONLY,MAYBEVOID), allow that the
497      *  new value can be set. It also causes the notification of listeners.
498      *  @param prop The property whose value is to be set.
499      *  @param value The new value for the property.
500      */
501     protected void setPropertyValue(Property prop, Object value) throws UnknownPropertyException,
502     PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
503     {
504         if ((prop.Attributes & PropertyAttribute.READONLY) == PropertyAttribute.READONLY)
505             throw new com.sun.star.beans.PropertyVetoException();
506         // The value may be null only if MAYBEVOID attribute is set
507         boolean bVoidValue= false;
508         if (value instanceof Any)
509             bVoidValue= ((Any) value).getObject() == null;
510         else
511             bVoidValue= value == null;
512         if (bVoidValue && (prop.Attributes & PropertyAttribute.MAYBEVOID) == 0)
513             throw new com.sun.star.lang.IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!");
514         if (bInDispose || bDisposed)
515             throw new DisposedException("Component is already disposed");
516 
517         //Check if the argument is allowed
518         boolean bValueOk= false;
519         if (value instanceof Any)
520             bValueOk= checkType(((Any) value).getObject());
521         else
522             bValueOk= checkType(value);
523         if (! bValueOk)
524             throw new com.sun.star.lang.IllegalArgumentException("No valid UNO type");
525 
526 
527         boolean bConversionOk= false;
528         Object[] outConvertedVal= new Object[1];
529         Object[] outOldValue= new Object[1];
530         synchronized (this)
531         {
532             bConversionOk= convertPropertyValue(prop, outConvertedVal, outOldValue, value);
533         }
534 
535         //The next step following the conversion is to set the new value of the property. Prior to this
536         // the XVetoableChangeListener s have to be notified.
537         if (bConversionOk)
538         {
539             // If the property is CONSTRAINED, then we must notify XVetoableChangeListener. The listener can throw a com.sun.star.lang.beans.PropertyVetoException which
540             // will cause this method to return (the exception is not caught here).
541             fire( new Property[]{prop}, outConvertedVal, outOldValue, true);
542 
543             synchronized (this)
544             {
545                 setPropertyValueNoBroadcast(prop, outConvertedVal[0]);
546             }
547             // fire a change event (XPropertyChangeListener, PropertyAttribute.BOUND
548             fire( new Property[]{prop}, outConvertedVal, outOldValue, false);
549         }
550     }
551 
552     /** Converts a value in a way so that it is appropriate for storing as a property value, that is
553      *  {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} can process the value without any further
554      *  conversion. This implementation presumes that
555      *  the values are stored in member variables of the furthest inheriting class. For example,
556      *  class A inherits this class then members of class A
557      *  can hold property values. If there is a class B which inherits A then only members of B can hold
558      *  property values. The variables must be public. A property must have been registered (e.g. by
559      *  {@link #registerProperty(Property, Object)} in order for this method to work. The identifyer argument (type Object)
560      *  used in the registerProperty methods must
561      *  be a java.lang.String, which is, the name of the member variable that holds the property value.
562      *  If one opts to store values differently then one may override
563      *  this method, as well as {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and
564      *  {@link #getPropertyValue(Property) getPropertyValue(Property)}.
565      *  This method is always called as a result of a call to one of the setter methods, such as
566      *  {@link #setPropertyValue(String,Object) XPropertySet.setPropertyValue},
567      *  {@link #setFastPropertyValue XFastPropertySet.setFastPropertyValue}
568      *  and {@link #setPropertyValues XMultiPropertySet.setPropertyValues}.
569      *  If this method fails, that is, it returns false or throws an exception, then no listeners are notified and the
570      *  property value, that was intended to be changed, remains untouched.<br /> This method does not have to deal with property attributes, such as
571      *  PropertyAttribute.READONLY or PropertyAttribute.MAYBEVOID. The processing of these attributes occurs
572      *  in the calling methods.<br />
573      *  Only if this method returns successfully further processing, such
574      *  as listener notification and finally the modifiction of the property's value, will occur.<br />
575      *
576      *  The actual modification of a property's value is done by {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast}
577      *  which is called subsequent to convertPropertyValue.
578      *<p>
579      *  This method converts values by help of the com.sun.star.uno.AnyConverter which only does a few widening
580      *  conversions on integer types and floating point types. For example, there is the property PropA with a Type equivalent
581      *  to int.class and the
582      *  value of the property is to be stored in a member variable of type int with name intProp. Then setPropertyValue is
583      *  called:
584      *  <pre>
585      *  set.setPropertyValue( "PropA", new Byte( (byte)111));
586      *  </pre>
587      *  At some point setPropertyValue will call convertPropertyValue and pass in the Byte object. Since we allow
588      *  that Byte values can be used with the property and know that the value is to be stored in intProp (type int)
589      *  we convert the Byte object into an Integer object which is then returned in the out-parameter <em>newVal</em>. This
590      *  conversion is actually performed by the AnyConverter. Later
591      *  the setPropertyValueNoBroadcast is called with that Integer object and the int value can be easily extracted
592      *  from the object and be assigned to the member intProp.
593      *  <p>
594      *  The method handles Any arguments the same as Object arguments. That is, the <em>setVal</em> argument can
595      *  be a java.lang.Boolean or a com.sun.star.uno.Any containing a java.lang.Boolean. Likewise, a member
596      *  containing a property value can be a com.sun.star.uno.Any or an java.lang.Object.
597      *  Then, no conversion is necessary, since they can hold all possible values. However, if
598      *  the member is an Object and <em>setVal</em> is an Any then the object contained in the any is assigned to
599      *  the member. The extra type information which exists as Type object in the Any will get lost. If this is not
600      *  intended then use an Any variable rather then an Object.<br />
601      *  If a member is an Object or Any and the argument <em>setVal</em> is an Object, other than String or array,
602      *  then it is presumed to be an UNO object and queried for XInterface. If successful, the out-param <em>newVal</em>
603      *  returns the XInterface.<br />
604      *  If a member is an UNO interface, then <em>setVal</em> is queried for this interface and the result is returned.
605      *  If <em>setVal</em> is null then <em>newVal</em> will be null too after return.
606      *  <p>
607      *  If a property value is stored using a primitive type the the out-parameters
608      *  <em>curVal</em> and <em>newVal</em> contain the respective wrapper class (e.g.java.lang.Byte, etc.).
609      *  curVal is used in calls to the XVetoableChangeListener and XPropertyChangeListener.
610      *
611      * @param property - in-param property for which the data is to be converted.
612      * @param newVal - out-param which contains the converted value on return.
613      * @param curVal - out-param the current value of the property. It is used in calls to the
614      *                   XVetoableChangeListener and XPropertyChangeListener.
615      *  @param setVal - in-param. The value that is to be converted so that it matches Property and the internally used
616      *  dataformat for that property.
617      *  @return true - Conversion was successful. <em>newVal</em> contains a valid value for the property. false -
618      *  conversion failed for some reason.
619      *  @throws com.sun.star.lang.IllegalArgumentException The value provided is unfit for the property.
620      *  @throws com.sun.star.lang.WrappedTargetException - An exception occured during the conversion, that is to be made known
621      *  to the caller.
622      */
623     protected boolean convertPropertyValue(Property property, Object[] newVal, Object[]curVal,  Object setVal)
624         throws com.sun.star.lang.IllegalArgumentException, WrappedTargetException, UnknownPropertyException
625     {
626         boolean ret= true;
627         try
628         {
629             // get the member name
630             String sMember= (String) getPropertyId(property);
631             if (sMember != null)
632             {
633                 // use reflection to obtain the field that holds the property value
634                 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
635                 // also get inherited fields, but only those which are public.
636                 Field propField= getClass().getDeclaredField(sMember);
637                 if (propField != null)
638                 {
639                     curVal[0]= propField.get(this);
640                     Class memberClass= propField.getType();
641 
642                     // MAYBEVOID: if setVal == null or it is an Any and getObject returns null, then a void value is to be set
643                     // This works only if there are no primitive types. For those we use the respective wrapper classes.
644                     // In this implementation, a null reference means void value.
645                     boolean bVoidValue= false;
646                     boolean bAnyVal= setVal instanceof Any;
647                     if (bAnyVal)
648                         bVoidValue= ((Any) setVal).getObject() == null;
649                     else
650                         bVoidValue= setVal == null;
651                     if (bVoidValue && memberClass.isPrimitive())
652                         throw new com.sun.star.lang.IllegalArgumentException("The implementation does not support the MAYBEVOID attribute for this property");
653 
654                     Object convObj= null;
655                     //The member that keeps the value of the Property is an Any. It can contain all possible
656                     //types, therefore a conversion is not necessary.
657                     if (memberClass.equals(Any.class))
658                     {
659                         if (bAnyVal)
660                             //parameter setVal is also an Any and can be used without further processing
661                             convObj= setVal;
662                         else
663                         {
664                             // Parameter setVal is not an Any. We need to construct an Any that contains
665                             // the argument setVal.
666                             // If setVal is an interface implementation then, we cannot constuct the
667                             // Any with setVal.getClass(), because the Any.Type._typeClass would be TypeClass.UNKNOWN.
668                             // We try to get an XInterface of setVal and set an XInterface type.
669                             if (setVal instanceof XInterface)
670                             {
671                                 XInterface xint= UnoRuntime.queryInterface(XInterface.class, setVal);
672                                 if (xint != null)
673                                     convObj= new Any(new Type(XInterface.class), xint);
674                             }
675                             // The member is an any, and the past in argument was null reference (MAYBEVOID is set)
676                             else if (setVal == null)
677                             {
678                                 // if the any member is still null we create a void any
679                                 if (curVal[0] == null)
680                                     convObj= new Any(new Type(), null);
681                                 else
682                                 {
683                                     //otherwise we create an Any with the same type as a value of null;
684                                     convObj= new Any( ((Any)curVal[0]).getType(), null);
685                                 }
686                             }
687                             else
688                                 convObj= new Any(new Type(setVal.getClass()), setVal);
689                         }
690                     }
691                     else
692                         convObj= convert(memberClass, setVal);
693                     newVal[0]= convObj;
694                 }
695             }
696             else
697                 throw new UnknownPropertyException("Property " + property.Name + " is unknown");
698         }
699         catch (java.lang.NoSuchFieldException e)
700         {
701             throw new WrappedTargetException("Field does not exist", this, e);
702         }
703         catch (java.lang.IllegalAccessException e)
704         {
705             throw new WrappedTargetException("", this ,e);
706         }
707         return ret;
708     }
709 
710     private boolean checkType(Object obj)
711     {
712         if (obj == null
713         || obj instanceof Boolean
714         || obj instanceof Character
715         || obj instanceof Number
716         || obj instanceof String
717         || obj instanceof XInterface
718         || obj instanceof Type
719         || obj instanceof com.sun.star.uno.Enum
720         || obj.getClass().isArray())
721             return true;
722         return false;
723     }
724 
725     // Param object can be an Any or other object. If obj is null then the return value is null
726     private Object convert( Class cl, Object obj) throws com.sun.star.lang.IllegalArgumentException
727     {
728         Object retVal= null;
729        //The member that keeps the value of the Property is an Object.Objects are similar to Anys in that they can
730        // hold all types.
731         if (obj == null || (obj instanceof Any && ((Any) obj).getObject() == null))
732             retVal= null;
733         else if(cl.equals(Object.class))
734         {
735             if (obj instanceof Any)
736                 obj= ((Any) obj).getObject();
737             retVal= obj;
738         }
739         else if(cl.equals(boolean.class))
740             retVal= new Boolean(AnyConverter.toBoolean(obj));
741         else if (cl.equals(char.class))
742             retVal= new Character(AnyConverter.toChar(obj));
743         else if (cl.equals(byte.class))
744             retVal= new Byte(AnyConverter.toByte(obj));
745         else if (cl.equals(short.class))
746             retVal= new Short(AnyConverter.toShort(obj));
747         else if (cl.equals(int.class))
748             retVal= new Integer(AnyConverter.toInt(obj));
749         else if (cl.equals(long.class))
750             retVal= new Long(AnyConverter.toLong(obj));
751         else if (cl.equals(float.class))
752             retVal= new Float(AnyConverter.toFloat(obj));
753         else if (cl.equals(double.class))
754             retVal= new Double(AnyConverter.toDouble(obj));
755         else if (cl.equals(String.class))
756             retVal= AnyConverter.toString(obj);
757         else if (cl.isArray())
758             retVal= AnyConverter.toArray(obj);
759         else if (cl.equals(Type.class))
760             retVal= AnyConverter.toType(obj);
761         else if (cl.equals(Boolean.class))
762             retVal= new Boolean(AnyConverter.toBoolean(obj));
763         else if (cl.equals(Character.class))
764             retVal= new Character(AnyConverter.toChar(obj));
765         else if (cl.equals(Byte.class))
766             retVal= new Byte(AnyConverter.toByte(obj));
767         else if (cl.equals(Short.class))
768             retVal= new Short(AnyConverter.toShort(obj));
769         else if (cl.equals(Integer.class))
770             retVal= new Integer(AnyConverter.toInt(obj));
771         else if (cl.equals(Long.class))
772             retVal= new Long(AnyConverter.toLong(obj));
773         else if (cl.equals(Float.class))
774             retVal= new Float(AnyConverter.toFloat(obj));
775         else if (cl.equals(Double.class))
776             retVal= new Double(AnyConverter.toDouble(obj));
777         else if (XInterface.class.isAssignableFrom(cl))
778             retVal= AnyConverter.toObject(new Type(cl), obj);
779         else if (com.sun.star.uno.Enum.class.isAssignableFrom(cl))
780             retVal= AnyConverter.toObject(new Type(cl), obj);
781         else
782             throw new com.sun.star.lang.IllegalArgumentException("Could not convert the argument");
783         return retVal;
784     }
785 
786     /**  Sets the value of a property. In this implementation property values are stored in member variables
787      *  (see {@link #convertPropertyValue convertPropertyValue} Notification of property listeners
788      *  does not occur in this method. By overriding this method one can take full control about how property values
789      *  are stored. But then, the {@link #convertPropertyValue convertPropertyValue} and
790      *  {@link #getPropertyValue(Property)} must be overridden too.
791      *
792      *  A Property with the MAYBEVOID attribute set, is stored as null value. Therefore the member variable must be
793      *  an Object in order to make use of the property attribute. An exception is Any. The Any variable can be initially null, but
794      *  once it is set the reference will not become null again. If the value is to be set to
795      *  void then a new Any will be stored
796      *  with a valid type but without a value (i.e. Any.getObject returns null).
797      *  If a property has the READONLY attribute set, and one of the setter methods, such as setPropertyValue, has been
798      *  called, then this method is not going to be called.
799      *  @param property the property for which the new value is set
800      *  @param value the new value for the property.
801      *  @throws com.sun.star.lang.WrappedTargetException An exception, which has to be made known to the caller,
802      *  occured during the setting of the value.
803      */
804     protected void setPropertyValueNoBroadcast(Property property, Object newVal)
805     throws WrappedTargetException
806     {
807         try
808         {
809             // get the member name
810             String sMember= (String) getPropertyId(property);
811             if (sMember != null)
812             {
813                 // use reflection to obtain the field that holds the property value
814                 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
815                 // also get inherited fields, but only those which are public.
816                 Field propField= getClass().getDeclaredField(sMember);
817                 if (propField != null)
818                     propField.set(this, newVal);
819             }
820         }
821         catch(java.lang.Exception e)
822         {
823             throw new WrappedTargetException("PropertySet.setPropertyValueNoBroadcast", this, e);
824         }
825     }
826     /** Retrieves the value of a property. This implementation presumes that the values are stored in member variables
827      *  of the furthest inheriting class (see {@link #convertPropertyValue convertPropertyValue}) and that the
828      *  variables are public. The property must have
829      *  been registered, for example by {@link #registerProperty(Property, Object)}. The identifyer Object argument
830      *  must have been a java.lang.String which was the name of the member variable holding the property value.
831      *  When properties are to be stored differently one has to override this method as well as
832      *  {@link #convertPropertyValue} and {@link #setPropertyValueNoBroadcast}. <br>
833      *  If a value is stored in a variable of a primitive type then this method returns an instance of the respective
834      *  wrapper class (e.g. java.lang.Boolean).
835      *  @param property The property for which the value is to be retrieved.
836      *  @return The value of the property.
837      */
838     protected Object getPropertyValue(Property property)
839     {
840         Object ret= null;
841         try
842         {
843             // get the member name
844             String sMember= (String) getPropertyId(property);
845             if (sMember != null)
846             {
847                 // use reflection to obtain the field that holds the property value
848                 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
849                 // also get inherited fields, but only those which are public.
850                 Field propField= getClass().getDeclaredField(sMember);
851                 if (propField != null)
852                     ret= propField.get(this);
853             }
854         }
855         catch(java.lang.NoSuchFieldException e)
856         {
857             throw new java.lang.RuntimeException(e);
858         }
859         catch(java.lang.IllegalAccessException e)
860         {
861             throw new java.lang.RuntimeException(e);
862         }
863         return ret;
864     }
865 
866     /**
867      *  This method fires events to XPropertyChangeListener,XVetoableChangeListener and
868      *  XPropertiesChangeListener event sinks.
869      *  To distinguish what listeners are to be called the argument <em>bVetoable</em> is to be set to true if
870      *  a XVetoableChangeListener is meant. For XPropertyChangeListener and XPropertiesChangeListener
871      *  it is to be set to false.
872      *
873      * @param properties	Properties wich will be or have been affected.
874      * @param newValues	the new values of the properties.
875      * @param oldValues	the old values of the properties.
876      * @param bVetoable true means fire to VetoableChangeListener, false means fire to
877      * XPropertyChangedListener and XMultiPropertyChangedListener.
878      */
879     protected void  fire(
880     Property[]  properties,
881     Object[] newValues,
882     Object[] oldValues,
883     boolean bVetoable ) throws PropertyVetoException
884     {
885         // Only fire, if one or more properties changed
886         int nNumProps= properties.length;
887         if (nNumProps > 0)
888         {
889             PropertyChangeEvent[] arEvts= new PropertyChangeEvent[nNumProps];
890             int nAffectedProps= 0;
891             // Loop over all changed properties to fill the event struct
892             for (int i= 0; i < nNumProps; i++)
893             {
894                 if ((bVetoable && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) > 0)
895                     || (!bVetoable && (properties[i].Attributes & PropertyAttribute.BOUND) > 0))
896                 {
897                     arEvts[i]= new PropertyChangeEvent(this, properties[i].Name, false,
898                                         properties[i].Handle, oldValues[i], newValues[i]);
899                     nAffectedProps++;
900                 }
901             }
902     		// fire the events for all changed properties
903             for (int i= 0; i < nAffectedProps; i++)
904             {
905     			// get the listener container for the property name
906                 InterfaceContainer lc= null;
907                 if (bVetoable)
908                     lc= aVetoableLC.getContainer(arEvts[i].PropertyName);
909                 else
910                     lc= aBoundLC.getContainer(arEvts[i].PropertyName);
911                 if (lc != null)
912                 {
913                     Iterator it= lc.iterator();
914                     while( it.hasNext())
915                     {
916                         Object listener= it.next();
917                         if (bVetoable)
918                             ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
919                         else
920                             ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
921                     }
922                 }
923        			// broadcast to all listeners with "" property name
924         		if(bVetoable)
925                     lc= listenerContainer.getContainer(XVetoableChangeListener.class);
926         		else
927             		lc= listenerContainer.getContainer(XPropertyChangeListener.class);
928     			if(lc != null)
929                 {
930     				Iterator it= lc.iterator();
931                     while(it.hasNext() )
932                     {
933                         Object listener= it.next();
934                         if( bVetoable ) // fire change Events?
935                             ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
936                         else
937                             ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
938                     }
939                 }
940             }
941             // fire at XPropertiesChangeListeners
942             // if nAffectedProps == 0 then there are no BOUND properties
943             if (!bVetoable && nAffectedProps > 0)
944             {
945 
946                 PropertyChangeEvent[] arReduced= new PropertyChangeEvent[nAffectedProps];
947                 System.arraycopy(arEvts, 0, arReduced, 0, nAffectedProps);
948                 InterfaceContainer lc= listenerContainer.getContainer(XPropertiesChangeListener.class);
949     			if (lc != null)
950         		{
951                     Iterator it= lc.iterator();
952                     while (it.hasNext())
953                     {
954                         XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next();
955     					// fire the hole event sequence to the XPropertiesChangeListener's
956                         listener.propertiesChange( arEvts );
957         			}
958             	}
959             }
960         }
961     }
962     // XFastPropertySet--------------------------------------------------------------------------------
963     public void setFastPropertyValue(int nHandle, Object aValue ) throws UnknownPropertyException,
964     PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
965     {
966         Property prop= getPropertyByHandle(nHandle);
967         if (prop == null)
968             throw new UnknownPropertyException(" The property with handle : " + nHandle +" is unknown");
969         setPropertyValue(prop, aValue);
970     }
971 
972     // XFastPropertySet --------------------------------------------------------------------------------
973     public Object getFastPropertyValue(int nHandle ) throws UnknownPropertyException,
974     WrappedTargetException
975     {
976         Property prop= getPropertyByHandle(nHandle);
977         if (prop == null)
978             throw new UnknownPropertyException("The property with handle : " + nHandle + " is unknown");
979         return getPropertyValue(prop);
980     }
981 
982     // XMultiPropertySet -----------------------------------------------------------------------------------
983     public void addPropertiesChangeListener(String[] propNames, XPropertiesChangeListener listener)
984     {
985         listenerContainer.addInterface(XPropertiesChangeListener.class, listener);
986     }
987 
988     // XMultiPropertySet -----------------------------------------------------------------------------------
989     public void firePropertiesChangeEvent(String[] propNames, XPropertiesChangeListener listener)
990     {
991         // Build the events.
992         PropertyChangeEvent[] arEvents= new PropertyChangeEvent[propNames.length];
993         int eventCount= 0;
994         // get a snapshot of the current property values
995         synchronized (this)
996         {
997             for (int i= 0; i < propNames.length; i++)
998             {
999                 Property prop= getProperty(propNames[i]);
1000                 if (prop != null)
1001                 {
1002                     Object value= null;
1003                     try
1004                     {
1005                        value= getPropertyValue(prop);
1006                     }
1007                     catch(Exception e)
1008                     {
1009                         continue;
1010                     }
1011                     arEvents[eventCount]= new PropertyChangeEvent(this, prop.Name,
1012                                         false, prop.Handle, value, value);
1013                     eventCount++;
1014                 }
1015             }
1016         }
1017 
1018         // fire events from unsynchronized section so as to prevent deadlocks
1019         if (eventCount > 0)
1020         {
1021             // Reallocate the array of the events if necessary
1022             if (arEvents.length != eventCount)
1023             {
1024                 PropertyChangeEvent[] arPropsTmp= new PropertyChangeEvent[eventCount];
1025                 System.arraycopy(arEvents, 0, arPropsTmp, 0, eventCount);
1026                 arEvents= arPropsTmp;
1027             }
1028             listener.propertiesChange(arEvents);
1029         }
1030     }
1031     // XMultiPropertySet -----------------------------------------------------------------------------------
1032     /** If a value for a property could not be retrieved then the respective element in the returned
1033      *  array has the value null.
1034      */
1035     public Object[] getPropertyValues(String[] propNames)
1036     {
1037         Object[] arValues= new Object[propNames.length];
1038         synchronized (this)
1039         {
1040             for (int i= 0; i < propNames.length; i++)
1041             {
1042                 Object value= null;
1043                 try
1044                 {
1045                     value= getPropertyValue(propNames[i]);
1046                 }
1047                 catch (Exception e)
1048                 {
1049                 }
1050                 arValues[i]= value;
1051             }
1052         }
1053         return arValues;
1054     }
1055     // XMultiPropertySet -----------------------------------------------------------------------------------
1056     public void removePropertiesChangeListener(XPropertiesChangeListener xPropertiesChangeListener)
1057     {
1058         listenerContainer.removeInterface(XPropertiesChangeListener.class, xPropertiesChangeListener);
1059     }
1060     // XMultiPropertySet -----------------------------------------------------------------------------------
1061     /** If the array of property names containes an unknown property then it will be ignored.
1062      */
1063     public void setPropertyValues(String[] propNames, Object[] values) throws PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException
1064     {
1065         for (int i= 0; i < propNames.length; i++)
1066         {
1067             try
1068             {
1069                 setPropertyValue(propNames[i], values[i]);
1070             }
1071             catch (UnknownPropertyException e)
1072             {
1073                 continue;
1074             }
1075 
1076         }
1077     }
1078 
1079     private class PropertySetInfo implements XPropertySetInfo
1080     {
1081         public com.sun.star.beans.Property[] getProperties()
1082         {
1083             return PropertySet.this.getProperties();
1084         }
1085 
1086         public com.sun.star.beans.Property getPropertyByName(String name) throws UnknownPropertyException
1087         {
1088             return getProperty(name);
1089         }
1090 
1091         public boolean hasPropertyByName(String name)
1092         {
1093             return getProperty(name) != null;
1094         }
1095 
1096     }
1097 }
1098 
1099 
1100 
1101 
1102 
1103