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