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