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