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