uno grid a11y: Use ImplInheritanceHelper
[LibreOffice.git] / ridljar / com / sun / star / lib / uno / helper / PropertySet.java
blobdec51b9b08fe5bb60eeb9b2a38f8432d178781bb
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 than 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 release 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 * @throws UnknownPropertyException
487 * See com.sun.star.beans.XPropertySet
488 * @throws PropertyVetoException
489 * See com.sun.star.beans.XPropertySet
490 * @throws WrappedTargetException
491 * See com.sun.star.beans.XPropertySet
493 protected void setPropertyValue(Property prop, Object value) throws UnknownPropertyException,
494 PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
496 if ((prop.Attributes & PropertyAttribute.READONLY) == PropertyAttribute.READONLY)
497 throw new com.sun.star.beans.PropertyVetoException();
498 // The value may be null only if MAYBEVOID attribute is set
499 boolean bVoidValue;
500 if (value instanceof Any)
501 bVoidValue= ((Any) value).getObject() == null;
502 else
503 bVoidValue= value == null;
504 if (bVoidValue && (prop.Attributes & PropertyAttribute.MAYBEVOID) == 0)
505 throw new com.sun.star.lang.IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!");
506 if (bInDispose || bDisposed)
507 throw new DisposedException("Component is already disposed");
509 //Check if the argument is allowed
510 boolean bValueOk;
511 if (value instanceof Any)
512 bValueOk= checkType(((Any) value).getObject());
513 else
514 bValueOk= checkType(value);
515 if (! bValueOk)
516 throw new com.sun.star.lang.IllegalArgumentException("No valid UNO type");
519 boolean bConversionOk= false;
520 Object[] outConvertedVal= new Object[1];
521 Object[] outOldValue= new Object[1];
522 synchronized (this)
524 bConversionOk= convertPropertyValue(prop, outConvertedVal, outOldValue, value);
527 //The next step following the conversion is to set the new value of the property. Prior to this
528 // the XVetoableChangeListener s have to be notified.
529 if (bConversionOk)
531 // If the property is CONSTRAINED, then we must notify XVetoableChangeListener. The listener can throw a com.sun.star.lang.beans.PropertyVetoException which
532 // will cause this method to return (the exception is not caught here).
533 fire( new Property[]{prop}, outConvertedVal, outOldValue, true);
535 synchronized (this)
537 setPropertyValueNoBroadcast(prop, outConvertedVal[0]);
539 // fire a change event (XPropertyChangeListener, PropertyAttribute.BOUND
540 fire( new Property[]{prop}, outConvertedVal, outOldValue, false);
544 /** Converts a value in a way so that it is appropriate for storing as a property value, that is
545 * {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} can process the value without any further
546 * conversion. This implementation presumes that
547 * the values are stored in member variables of the furthest inheriting class. For example,
548 * class A inherits this class then members of class A
549 * can hold property values. If there is a class B which inherits A then only members of B can hold
550 * property values. The variables must be public. A property must have been registered (e.g. by
551 * {@link #registerProperty(Property, Object)} in order for this method to work. The identifier argument (type Object)
552 * used in the registerProperty methods must
553 * be a String, which is, the name of the member variable that holds the property value.
554 * If one opts to store values differently then one may override
555 * this method, as well as {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast} and
556 * {@link #getPropertyValue(Property) getPropertyValue(Property)}.
557 * This method is always called as a result of a call to one of the setter methods, such as
558 * {@link #setPropertyValue(String,Object) XPropertySet.setPropertyValue},
559 * {@link #setFastPropertyValue XFastPropertySet.setFastPropertyValue}
560 * and {@link #setPropertyValues XMultiPropertySet.setPropertyValues}.
561 * If this method fails, that is, it returns false or throws an exception, then no listeners are notified and the
562 * property value, that was intended to be changed, remains untouched.
564 * This method does not have to deal with property attributes, such as
565 * PropertyAttribute.READONLY or PropertyAttribute.MAYBEVOID. The processing of these attributes occurs
566 * in the calling methods.
568 * Only if this method returns successfully further processing, such
569 * as listener notification and finally the modification of the property's value, will occur.
571 * The actual modification of a property's value is done by {@link #setPropertyValueNoBroadcast setPropertyValueNoBroadcast}
572 * which is called subsequent to convertPropertyValue.
573 *<p>
574 * This method converts values by help of the com.sun.star.uno.AnyConverter which only does a few widening
575 * conversions on integer types and floating point types. For example, there is the property PropA with a Type equivalent
576 * to int.class and the
577 * value of the property is to be stored in a member variable of type int with name intProp. Then setPropertyValue is
578 * called:
579 * <pre>
580 * set.setPropertyValue( "PropA", Byte.valueOf( (byte)111));
581 * </pre>
582 * At some point setPropertyValue will call convertPropertyValue and pass in the Byte object. Since we allow
583 * that Byte values can be used with the property and know that the value is to be stored in intProp (type int)
584 * we convert the Byte object into an Integer object which is then returned in the out-parameter <em>newVal</em>. This
585 * conversion is actually performed by the AnyConverter. Later
586 * the setPropertyValueNoBroadcast is called with that Integer object and the int value can be easily extracted
587 * from the object and be assigned to the member intProp.
588 * <p>
589 * The method handles Any arguments the same as Object arguments. That is, the <em>setVal</em> argument can
590 * be a java.lang.Boolean or a com.sun.star.uno.Any containing a java.lang.Boolean. Likewise, a member
591 * containing a property value can be a com.sun.star.uno.Any or a java.lang.Object.
592 * Then, no conversion is necessary, since they can hold all possible values. However, if
593 * the member is an Object and <em>setVal</em> is an Any then the object contained in the any is assigned to
594 * the member. The extra type information which exists as Type object in the Any will get lost. If this is not
595 * intended then use an Any variable rather than an Object.
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 a UNO object and queried for XInterface. If successful, the out-param <em>newVal</em>
599 * returns the XInterface.
601 * If a member is a UNO interface, then <em>setVal</em> is queried for this interface and the result is returned.
602 * If <em>setVal</em> is null then <em>newVal</em> will be null too after return.
603 * <p>
604 * If a property value is stored using a primitive type the out-parameters
605 * <em>curVal</em> and <em>newVal</em> contain the respective wrapper class (e.g.java.lang.Byte, etc.).
606 * curVal is used in calls to the XVetoableChangeListener and XPropertyChangeListener.
608 * @param property - in-param property for which the data is to be converted.
609 * @param newVal - out-param which contains the converted value on return.
610 * @param curVal - out-param the current value of the property. It is used in calls to the
611 * XVetoableChangeListener and XPropertyChangeListener.
612 * @param setVal - in-param. The value that is to be converted so that it matches Property and the internally used
613 * dataformat for that property.
614 * @return true - Conversion was successful. <em>newVal</em> contains a valid value for the property. false -
615 * conversion failed for some reason.
617 * @throws UnknownPropertyException
618 * See com.sun.star.beans.XPropertySet
619 * @throws com.sun.star.lang.IllegalArgumentException The value provided is unfit for the property.
620 * @throws com.sun.star.lang.WrappedTargetException - An exception occurred during the conversion, that is to be made known
621 * to the caller.
623 protected boolean convertPropertyValue(Property property, Object[] newVal, Object[]curVal, Object setVal)
624 throws com.sun.star.lang.IllegalArgumentException, WrappedTargetException, UnknownPropertyException
626 boolean ret= true;
629 // get the member name
630 String sMember= (String) getPropertyId(property);
631 if (sMember != null)
633 // use reflection to obtain the field that holds the property value
634 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
635 // also get inherited fields, but only those which are public.
636 Field propField= getClass().getDeclaredField(sMember);
637 if (propField != null)
639 curVal[0]= propField.get(this);
640 Class memberClass= propField.getType();
642 // MAYBEVOID: if setVal == null or it is an Any and getObject returns null, then a void value is to be set
643 // This works only if there are no primitive types. For those we use the respective wrapper classes.
644 // In this implementation, a null reference means void value.
645 boolean bVoidValue= false;
646 boolean bAnyVal= setVal instanceof Any;
647 if (bAnyVal)
648 bVoidValue= ((Any) setVal).getObject() == null;
649 else
650 bVoidValue= setVal == null;
651 if (bVoidValue && memberClass.isPrimitive())
652 throw new com.sun.star.lang.IllegalArgumentException("The implementation does not support the MAYBEVOID attribute for this property");
654 Object convObj= null;
655 //The member that keeps the value of the Property is an Any. It can contain all possible
656 //types, therefore a conversion is not necessary.
657 if (memberClass.equals(Any.class))
659 if (bAnyVal)
660 //parameter setVal is also an Any and can be used without further processing
661 convObj= setVal;
662 else
664 // Parameter setVal is not an Any. We need to construct an Any that contains
665 // the argument setVal.
666 // If setVal is an interface implementation then, we cannot construct the
667 // Any with setVal.getClass(), because the Any.Type._typeClass would be TypeClass.UNKNOWN.
668 // We try to get an XInterface of setVal and set an XInterface type.
669 if (setVal instanceof XInterface)
671 XInterface xint= UnoRuntime.queryInterface(XInterface.class, setVal);
672 if (xint != null)
673 convObj= new Any(new Type(XInterface.class), xint);
675 // The member is an any, and the past in argument was null reference (MAYBEVOID is set)
676 else if (setVal == null)
678 // if the any member is still null we create a void any
679 if (curVal[0] == null)
680 convObj= new Any(new Type(), null);
681 else
683 //otherwise we create an Any with the same type as a value of null;
684 convObj= new Any( ((Any)curVal[0]).getType(), null);
687 else
688 convObj= new Any(new Type(setVal.getClass()), setVal);
691 else
692 convObj= convert(memberClass, setVal);
693 newVal[0]= convObj;
696 else
697 throw new UnknownPropertyException("Property " + property.Name + " is unknown");
699 catch (java.lang.NoSuchFieldException e)
701 throw new WrappedTargetException(e, "Field does not exist", this, e);
703 catch (java.lang.IllegalAccessException e)
705 throw new WrappedTargetException(e, "", this ,e);
707 return ret;
710 private boolean checkType(Object obj)
712 return obj == null
713 || obj instanceof Boolean
714 || obj instanceof Character
715 || obj instanceof Number
716 || obj instanceof String
717 || obj instanceof XInterface
718 || obj instanceof Type
719 || obj instanceof com.sun.star.uno.Enum
720 || obj.getClass().isArray();
723 // Param object can be an Any or other object. If obj is null then the return value is null
724 private Object convert( Class cl, Object obj) throws com.sun.star.lang.IllegalArgumentException
726 Object retVal= null;
727 //The member that keeps the value of the Property is an Object.Objects are similar to Anys in that they can
728 // hold all types.
729 if (obj == null || (obj instanceof Any && ((Any) obj).getObject() == null))
731 else if(cl.equals(Object.class))
733 if (obj instanceof Any)
734 obj= ((Any) obj).getObject();
735 retVal= obj;
737 else if(cl.equals(boolean.class))
738 retVal= Boolean.valueOf(AnyConverter.toBoolean(obj));
739 else if (cl.equals(char.class))
740 retVal= Character.valueOf(AnyConverter.toChar(obj));
741 else if (cl.equals(byte.class))
742 retVal= Byte.valueOf(AnyConverter.toByte(obj));
743 else if (cl.equals(short.class))
744 retVal= Short.valueOf(AnyConverter.toShort(obj));
745 else if (cl.equals(int.class))
746 retVal= Integer.valueOf(AnyConverter.toInt(obj));
747 else if (cl.equals(long.class))
748 retVal= Long.valueOf(AnyConverter.toLong(obj));
749 else if (cl.equals(float.class))
750 retVal= Float.valueOf(AnyConverter.toFloat(obj));
751 else if (cl.equals(double.class))
752 retVal= Double.valueOf(AnyConverter.toDouble(obj));
753 else if (cl.equals(String.class))
754 retVal= AnyConverter.toString(obj);
755 else if (cl.isArray())
756 retVal= AnyConverter.toArray(obj);
757 else if (cl.equals(Type.class))
758 retVal= AnyConverter.toType(obj);
759 else if (cl.equals(Boolean.class))
760 retVal= Boolean.valueOf(AnyConverter.toBoolean(obj));
761 else if (cl.equals(Character.class))
762 retVal= Character.valueOf(AnyConverter.toChar(obj));
763 else if (cl.equals(Byte.class))
764 retVal= Byte.valueOf(AnyConverter.toByte(obj));
765 else if (cl.equals(Short.class))
766 retVal= Short.valueOf(AnyConverter.toShort(obj));
767 else if (cl.equals(Integer.class))
768 retVal= Integer.valueOf(AnyConverter.toInt(obj));
769 else if (cl.equals(Long.class))
770 retVal= Long.valueOf(AnyConverter.toLong(obj));
771 else if (cl.equals(Float.class))
772 retVal= Float.valueOf(AnyConverter.toFloat(obj));
773 else if (cl.equals(Double.class))
774 retVal= Double.valueOf(AnyConverter.toDouble(obj));
775 else if (XInterface.class.isAssignableFrom(cl))
776 retVal= AnyConverter.toObject(new Type(cl), obj);
777 else if (com.sun.star.uno.Enum.class.isAssignableFrom(cl))
778 retVal= AnyConverter.toObject(new Type(cl), obj);
779 else
780 throw new com.sun.star.lang.IllegalArgumentException("Could not convert the argument");
781 return retVal;
784 /** Sets the value of a property. In this implementation property values are stored in member variables
785 * (see {@link #convertPropertyValue convertPropertyValue} Notification of property listeners
786 * does not occur in this method. By overriding this method one can take full control about how property values
787 * are stored. But then, the {@link #convertPropertyValue convertPropertyValue} and
788 * {@link #getPropertyValue(Property)} must be overridden too.
790 * A Property with the MAYBEVOID attribute set, is stored as null value. Therefore the member variable must be
791 * an Object in order to make use of the property attribute. An exception is Any. The Any variable can be initially null, but
792 * once it is set the reference will not become null again. If the value is to be set to
793 * void then a new Any will be stored
794 * with a valid type but without a value (i.e. Any.getObject returns null).
795 * If a property has the READONLY attribute set, and one of the setter methods, such as setPropertyValue, has been
796 * called, then this method is not going to be called.
797 * @param property the property for which the new value is set
798 * @param newVal the new value for the property.
799 * @throws com.sun.star.lang.WrappedTargetException An exception, which has to be made known to the caller,
800 * occurred during the setting of the value.
802 protected void setPropertyValueNoBroadcast(Property property, Object newVal)
803 throws WrappedTargetException
807 // get the member name
808 String sMember= (String) getPropertyId(property);
809 if (sMember != null)
811 // use reflection to obtain the field that holds the property value
812 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
813 // also get inherited fields, but only those which are public.
814 Field propField= getClass().getDeclaredField(sMember);
815 if (propField != null)
816 propField.set(this, newVal);
819 catch(java.lang.Exception e)
821 throw new WrappedTargetException(e, "PropertySet.setPropertyValueNoBroadcast", this, e);
824 /** Retrieves the value of a property. This implementation presumes that the values are stored in member variables
825 * of the furthest inheriting class (see {@link #convertPropertyValue convertPropertyValue}) and that the
826 * variables are public. The property must have
827 * been registered, for example by {@link #registerProperty(Property, Object)}. The identifier Object argument
828 * must have been a String which was the name of the member variable holding the property value.
829 * When properties are to be stored differently one has to override this method as well as
830 * {@link #convertPropertyValue} and {@link #setPropertyValueNoBroadcast}. <br>
831 * If a value is stored in a variable of a primitive type then this method returns an instance of the respective
832 * wrapper class (e.g. java.lang.Boolean).
833 * @param property The property for which the value is to be retrieved.
834 * @return The value of the property.
836 protected Object getPropertyValue(Property property)
838 Object ret= null;
841 // get the member name
842 String sMember= (String) getPropertyId(property);
843 if (sMember != null)
845 // use reflection to obtain the field that holds the property value
846 // Class.getDeclaredFields does not return inherited fields. One could use Class.getFields to
847 // also get inherited fields, but only those which are public.
848 Field propField= getClass().getDeclaredField(sMember);
849 if (propField != null)
850 ret= propField.get(this);
853 catch(java.lang.NoSuchFieldException e)
855 throw new java.lang.RuntimeException(e);
857 catch(java.lang.IllegalAccessException e)
859 throw new java.lang.RuntimeException(e);
861 return ret;
865 * This method fires events to XPropertyChangeListener,XVetoableChangeListener and
866 * XPropertiesChangeListener event sinks.
867 * To distinguish what listeners are to be called the argument <em>bVetoable</em> is to be set to true if
868 * a XVetoableChangeListener is meant. For XPropertyChangeListener and XPropertiesChangeListener
869 * it is to be set to false.
871 * @param properties Properties which will be or have been affected.
872 * @param newValues the new values of the properties.
873 * @param oldValues the old values of the properties.
874 * @param bVetoable true means fire to VetoableChangeListener, false means fire to
875 * XPropertyChangedListener and XMultiPropertyChangedListener.
877 * @throws PropertyVetoException
878 * if a vetoable listener throws it.
880 protected void fire(
881 Property[] properties,
882 Object[] newValues,
883 Object[] oldValues,
884 boolean bVetoable ) throws PropertyVetoException
886 // Only fire, if one or more properties changed
887 int nNumProps= properties.length;
888 if (nNumProps > 0)
890 PropertyChangeEvent[] arEvts= new PropertyChangeEvent[nNumProps];
891 int nAffectedProps= 0;
892 // Loop over all changed properties to fill the event struct
893 for (int i= 0; i < nNumProps; i++)
895 if ((bVetoable && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) > 0)
896 || (!bVetoable && (properties[i].Attributes & PropertyAttribute.BOUND) > 0))
898 arEvts[i]= new PropertyChangeEvent(this, properties[i].Name, false,
899 properties[i].Handle, oldValues[i], newValues[i]);
900 nAffectedProps++;
903 // fire the events for all changed properties
904 for (int i= 0; i < nAffectedProps; i++)
906 // get the listener container for the property name
907 InterfaceContainer lc;
908 if (bVetoable)
909 lc= aVetoableLC.getContainer(arEvts[i].PropertyName);
910 else
911 lc= aBoundLC.getContainer(arEvts[i].PropertyName);
912 Iterator it = lc != null ? lc.iterator() : null;
913 if (it != null)
915 while( it.hasNext())
917 Object listener= it.next();
918 if (bVetoable)
919 ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
920 else
921 ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
924 // broadcast to all listeners with "" property name
925 if(bVetoable)
926 lc= listenerContainer.getContainer(XVetoableChangeListener.class);
927 else
928 lc= listenerContainer.getContainer(XPropertyChangeListener.class);
929 it = lc != null ? lc.iterator() : null;
930 if (it != null)
932 while(it.hasNext() )
934 Object listener= it.next();
935 if( bVetoable ) // fire change Events?
936 ((XVetoableChangeListener) listener).vetoableChange(arEvts[i]);
937 else
938 ((XPropertyChangeListener) listener).propertyChange(arEvts[i]);
942 // fire at XPropertiesChangeListeners
943 // if nAffectedProps == 0 then there are no BOUND properties
944 if (!bVetoable && nAffectedProps > 0)
947 PropertyChangeEvent[] arReduced= new PropertyChangeEvent[nAffectedProps];
948 System.arraycopy(arEvts, 0, arReduced, 0, nAffectedProps);
949 InterfaceContainer lc= listenerContainer.getContainer(XPropertiesChangeListener.class);
950 Iterator it = lc != null ? lc.iterator() : null;
951 if (it != null)
953 while (it.hasNext())
955 XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next();
956 // fire the whole event sequence to the XPropertiesChangeListener's
957 listener.propertiesChange( arEvts );
963 // XFastPropertySet--------------------------------------------------------------------------------
964 public void setFastPropertyValue(int nHandle, Object aValue ) throws UnknownPropertyException,
965 PropertyVetoException, com.sun.star.lang.IllegalArgumentException, WrappedTargetException
967 Property prop= getPropertyByHandle(nHandle);
968 if (prop == null)
969 throw new UnknownPropertyException(" The property with handle : " + nHandle +" is unknown");
970 setPropertyValue(prop, aValue);
973 // XFastPropertySet --------------------------------------------------------------------------------
974 public Object getFastPropertyValue(int nHandle ) throws UnknownPropertyException,
975 WrappedTargetException
977 Property prop= getPropertyByHandle(nHandle);
978 if (prop == null)
979 throw new UnknownPropertyException("The property with handle : " + nHandle + " is unknown");
980 return getPropertyValue(prop);
983 // XMultiPropertySet -----------------------------------------------------------------------------------
984 public void addPropertiesChangeListener(String[] propNames, XPropertiesChangeListener listener)
986 listenerContainer.addInterface(XPropertiesChangeListener.class, listener);
989 // XMultiPropertySet -----------------------------------------------------------------------------------
990 public void firePropertiesChangeEvent(String[] propNames, XPropertiesChangeListener listener)
992 // Build the events.
993 PropertyChangeEvent[] arEvents= new PropertyChangeEvent[propNames.length];
994 int eventCount= 0;
995 // get a snapshot of the current property values
996 synchronized (this)
998 for (int i= 0; i < propNames.length; i++)
1000 Property prop= getProperty(propNames[i]);
1001 if (prop != null)
1003 Object value= null;
1006 value= getPropertyValue(prop);
1008 catch(Exception e)
1010 continue;
1012 arEvents[eventCount]= new PropertyChangeEvent(this, prop.Name,
1013 false, prop.Handle, value, value);
1014 eventCount++;
1019 // fire events from unsynchronized section so as to prevent deadlocks
1020 if (eventCount > 0)
1022 // Reallocate the array of the events if necessary
1023 if (arEvents.length != eventCount)
1025 PropertyChangeEvent[] arPropsTmp= new PropertyChangeEvent[eventCount];
1026 System.arraycopy(arEvents, 0, arPropsTmp, 0, eventCount);
1027 arEvents= arPropsTmp;
1029 listener.propertiesChange(arEvents);
1032 // XMultiPropertySet -----------------------------------------------------------------------------------
1033 /** If a value for a property could not be retrieved then the respective element in the returned
1034 * array has the value null.
1036 public Object[] getPropertyValues(String[] propNames)
1038 Object[] arValues= new Object[propNames.length];
1039 synchronized (this)
1041 for (int i= 0; i < propNames.length; i++)
1043 Object value= null;
1046 value= getPropertyValue(propNames[i]);
1048 catch (Exception e)
1051 arValues[i]= value;
1054 return arValues;
1056 // XMultiPropertySet -----------------------------------------------------------------------------------
1057 public void removePropertiesChangeListener(XPropertiesChangeListener xPropertiesChangeListener)
1059 listenerContainer.removeInterface(XPropertiesChangeListener.class, xPropertiesChangeListener);
1061 // XMultiPropertySet -----------------------------------------------------------------------------------
1062 /** If the array of property names contains an unknown property then it will be ignored.
1064 public void setPropertyValues(String[] propNames, Object[] values) throws PropertyVetoException, com.sun.star.lang.IllegalArgumentException, com.sun.star.lang.WrappedTargetException
1066 for (int i= 0; i < propNames.length; i++)
1070 setPropertyValue(propNames[i], values[i]);
1072 catch (UnknownPropertyException e)
1074 continue;
1080 private class PropertySetInfo implements XPropertySetInfo
1082 public com.sun.star.beans.Property[] getProperties()
1084 return PropertySet.this.getProperties();
1087 public com.sun.star.beans.Property getPropertyByName(String name) throws UnknownPropertyException
1089 return getProperty(name);
1092 public boolean hasPropertyByName(String name)
1094 return getProperty(name) != null;