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