1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 package com.sun.star.lib.uno.helper; 29 30 import com.sun.star.beans.Property; 31 import com.sun.star.beans.PropertyAttribute; 32 import com.sun.star.beans.PropertyChangeEvent; 33 import com.sun.star.beans.PropertyState; 34 import com.sun.star.beans.PropertyValue; 35 import com.sun.star.beans.PropertyVetoException; 36 import com.sun.star.beans.UnknownPropertyException; 37 import com.sun.star.beans.XPropertyChangeListener; 38 import com.sun.star.beans.XPropertySetInfo; 39 import com.sun.star.beans.XVetoableChangeListener; 40 import com.sun.star.container.NoSuchElementException; 41 import com.sun.star.container.XHierarchicalNameAccess; 42 import com.sun.star.lang.DisposedException; 43 import com.sun.star.lang.EventObject; 44 import com.sun.star.lang.WrappedTargetException; 45 import com.sun.star.lang.WrappedTargetRuntimeException; 46 import com.sun.star.lang.XComponent; 47 import com.sun.star.reflection.XCompoundTypeDescription; 48 import com.sun.star.reflection.XIdlClass; 49 import com.sun.star.reflection.XIdlField2; 50 import com.sun.star.reflection.XIdlReflection; 51 import com.sun.star.reflection.XIndirectTypeDescription; 52 import com.sun.star.reflection.XInterfaceAttributeTypeDescription2; 53 import com.sun.star.reflection.XInterfaceMemberTypeDescription; 54 import com.sun.star.reflection.XInterfaceTypeDescription2; 55 import com.sun.star.reflection.XStructTypeDescription; 56 import com.sun.star.reflection.XTypeDescription; 57 import com.sun.star.uno.Any; 58 import com.sun.star.uno.AnyConverter; 59 import com.sun.star.uno.DeploymentException; 60 import com.sun.star.uno.Type; 61 import com.sun.star.uno.TypeClass; 62 import com.sun.star.uno.UnoRuntime; 63 import com.sun.star.uno.XComponentContext; 64 import com.sun.star.uno.XInterface; 65 import java.util.ArrayList; 66 import java.util.HashMap; 67 import java.util.HashSet; 68 import java.util.Iterator; 69 import java.util.Map; 70 import java.util.Vector; 71 72 /** 73 A helper mixin to implement certain UNO interfaces related to property set 74 handling on top of the attributes of a given UNO interface type. 75 76 <p>A client will mix in this class by keeping a reference to an instance of 77 this class, and forwarding all methods of (a subset of the interfaces) 78 <code>com.sun.star.beans.XPropertySet</code>, 79 <code>com.sun.star.beans.XFastPropertySet</code>, and 80 <code>com.sun.star.beans.XPropertyAccess</code> to it.</p> 81 82 <p>Client code should not use the monitors associated with instances of this 83 class, as they are used for internal purposes.</p> 84 85 @since UDK 3.2 86 */ 87 public final class PropertySetMixin { 88 /** 89 The constructor. 90 91 @param context the component context used by this instance; must not be 92 null, and must supply the service 93 <code>com.sun.star.reflection.CoreReflection</code> and the singleton 94 <code>com.sun.star.reflection.theTypeDescriptionManager</code> 95 96 @param object the client UNO object into which this instance is mixed in; 97 must not be null, and must support the given <code>type</code> 98 99 @param type the UNO interface type whose attributes are mapped to 100 properties; must not be null, and must represent a UNO interface type 101 102 @param absentOptional a list of optional properties that are not present, 103 and should thus not be visible via 104 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>, 105 <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>, 106 <code>com.sun.star.beans.XPropertySet.removePropertyChangeListener<!-- 107 --></code>, 108 <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>, 109 and <code>com.sun.star.beans.XPropertySet.<!-- 110 -->removeVetoableChangeListener</code>; null is treated the same as an 111 empty list; if non-null, the given array must not be modified after it is 112 passed to this constructor. For consistency reasons, the given 113 <code>absentOptional</code> should only contain the names of attributes 114 that represent optional properties that are not present (that is, the 115 attribute getters and setters always throw a 116 <code>com.sun.star.beans.UnknownPropertyException</code>), and should 117 contain each such name only once. If an optional property is not present 118 (that is, the corresponding attribute getter and setter always throw a 119 <code>com.sun.star.beans.UnknownPropertyException</code>) but is not 120 contained in the given <code>absentOptional</code>, then it will be 121 visible via 122 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code> as a 123 <code>com.sun.star.beans.Property</code> with a set 124 <code>com.sun.star.beans.PropertyAttribute.OPTIONAL</code>. If the given 125 <code>object</code> does not implement 126 <code>com.sun.star.beans.XPropertySet</code>, then the given 127 <code>absentOptional</code> is effectively ignored and can be null or 128 empty. 129 */ 130 public PropertySetMixin( 131 XComponentContext context, XInterface object, Type type, 132 String[] absentOptional) 133 { 134 // assert context != null && object != null && type != null 135 // && type.getTypeClass() == TypeClass.INTERFACE; 136 this.context = context; 137 this.object = object; 138 this.type = type; 139 this.absentOptional = absentOptional; 140 idlClass = getReflection(type.getTypeName()); 141 XTypeDescription ifc; 142 try { 143 ifc = UnoRuntime.queryInterface( 144 XTypeDescription.class, 145 (UnoRuntime.queryInterface( 146 XHierarchicalNameAccess.class, 147 context.getValueByName( 148 "/singletons/com.sun.star.reflection." 149 + "theTypeDescriptionManager")). 150 getByHierarchicalName(type.getTypeName()))); 151 } catch (NoSuchElementException e) { 152 throw new RuntimeException( 153 "unexpected com.sun.star.container.NoSuchElementException: " 154 + e.getMessage()); 155 } 156 HashMap map = new HashMap(); 157 ArrayList handleNames = new ArrayList(); 158 initProperties(ifc, map, handleNames, new HashSet()); 159 properties = map; 160 handleMap = (String[]) handleNames.toArray( 161 new String[handleNames.size()]); 162 } 163 164 /** 165 A method used by clients when implementing UNO interface type attribute 166 setter functions. 167 168 <p>First, this method checks whether this instance has already been 169 disposed (see {@link #dispose}), and throws a 170 <code>com.sun.star.beans.DisposedException</code> if applicable. For a 171 constrained attribute (whose setter can explicitly raise 172 <code>com.sun.star.beans.PropertyVetoException</code>), this method 173 notifies any <code>com.sun.star.beans.XVetoableChangeListener</code>s. 174 For a bound attribute, this method modifies the passed-in 175 <code>bound</code> so that it can afterwards be used to notify any 176 <code>com.sun.star.beans.XPropertyChangeListener</code>s. This method 177 should be called before storing the new attribute value, and 178 <code>bound.notifyListeners()</code> should be called exactly once after 179 storing the new attribute value (in case the attribute is bound; 180 otherwise, calling <code>bound.notifyListeners()</code> is ignored). 181 Furthermore, <code>bound.notifyListeners()</code> and this method have to 182 be called from the same thread.</p> 183 184 @param propertyName the name of the property (which is the same as the 185 name of the attribute that is going to be set) 186 187 @param oldValue the property value corresponding to the old attribute 188 value. This is only used as 189 <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code>, which is 190 rather useless, anyway (see “Using the Observer Pattern” in 191 <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> 192 <cite>OpenOffice.org Coding Guidelines</cite></a>). If the attribute 193 that is going to be set is neither bound nor constrained, or if 194 <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code> should not 195 be set, {@link Any#VOID} can be used instead. 196 197 @param newValue the property value corresponding to the new 198 attribute value. This is only used as 199 <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code>, which is 200 rather useless, anyway (see “Using the Observer Pattern&rdquo: in 201 <a href="http://tools.openoffice.org/CodingGuidelines.sxw"> 202 <cite>OpenOffice.org Coding Guidelines</cite></a>), <em>unless</em> the 203 attribute that is going to be set is constrained. If the attribute 204 that is going to be set is neither bound nor constrained, or if it is 205 only bound but 206 <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code> should not 207 be set, {@link Any#VOID} can be used instead. 208 209 @param bound a reference to a fresh {@link BoundListeners} instance 210 (which has not been passed to this method before, and on which 211 {@link BoundListeners#notifyListeners} has not yet been called); may only 212 be null if the attribute that is going to be set is not bound 213 */ 214 public void prepareSet( 215 String propertyName, Object oldValue, Object newValue, 216 BoundListeners bound) 217 throws PropertyVetoException 218 { 219 // assert properties.get(propertyName) != null; 220 Property p = ((PropertyData) properties.get(propertyName)).property; 221 Vector specificVeto = null; 222 Vector unspecificVeto = null; 223 synchronized (this) { 224 if (disposed) { 225 throw new DisposedException("disposed", object); 226 } 227 if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { 228 Object o = vetoListeners.get(propertyName); 229 if (o != null) { 230 specificVeto = (Vector) ((Vector) o).clone(); 231 } 232 o = vetoListeners.get(""); 233 if (o != null) { 234 unspecificVeto = (Vector) ((Vector) o).clone(); 235 } 236 } 237 if ((p.Attributes & PropertyAttribute.BOUND) != 0) { 238 // assert bound != null; 239 Object o = boundListeners.get(propertyName); 240 if (o != null) { 241 bound.specificListeners = (Vector) ((Vector) o).clone(); 242 } 243 o = boundListeners.get(""); 244 if (o != null) { 245 bound.unspecificListeners = (Vector) ((Vector) o).clone(); 246 } 247 } 248 } 249 if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) { 250 PropertyChangeEvent event = new PropertyChangeEvent( 251 object, propertyName, false, p.Handle, oldValue, newValue); 252 if (specificVeto != null) { 253 for (Iterator i = specificVeto.iterator(); i.hasNext();) { 254 try { 255 ((XVetoableChangeListener) i.next()).vetoableChange( 256 event); 257 } catch (DisposedException e) {} 258 } 259 } 260 if (unspecificVeto != null) { 261 for (Iterator i = unspecificVeto.iterator(); i.hasNext();) { 262 try { 263 ((XVetoableChangeListener) i.next()).vetoableChange( 264 event); 265 } catch (DisposedException e) {} 266 } 267 } 268 } 269 if ((p.Attributes & PropertyAttribute.BOUND) != 0) { 270 // assert bound != null; 271 bound.event = new PropertyChangeEvent( 272 object, propertyName, false, p.Handle, oldValue, newValue); 273 } 274 } 275 276 /** 277 A simplified version of {@link #prepareSet(String, Object, Object, 278 PropertySetMixin.BoundListeners)}. 279 280 <p>This method is useful for attributes that are not constrained.</p> 281 282 @param propertyName the name of the property (which is the same as the 283 name of the attribute that is going to be set) 284 285 @param bound a reference to a fresh {@link BoundListeners} instance 286 (which has not been passed to this method before, and on which 287 {@link BoundListeners#notifyListeners} has not yet been called); may only 288 be null if the attribute that is going to be set is not bound 289 */ 290 public void prepareSet(String propertyName, BoundListeners bound) { 291 try { 292 prepareSet(propertyName, Any.VOID, Any.VOID, bound); 293 } catch (PropertyVetoException e) { 294 throw new RuntimeException("unexpected " + e); 295 } 296 } 297 298 /** 299 Marks this instance as being disposed. 300 301 <p>See <code>com.sun.star.lang.XComponent</code> for the general concept 302 of disposing UNO objects. On the first call to this method, all 303 registered listeners 304 (<code>com.sun.star.beans.XPropertyChangeListener</code>s and 305 <code>com.sun.star.beans.XVetoableChangeListener</code>s) are notified of 306 the disposing source. Any subsequent calls to this method are 307 ignored.</p> 308 */ 309 public void dispose() { 310 HashMap bound; 311 HashMap veto; 312 synchronized (this) { 313 bound = boundListeners; 314 boundListeners = null; 315 veto = vetoListeners; 316 vetoListeners = null; 317 disposed = true; 318 } 319 EventObject event = new EventObject(object); 320 if (bound != null) { 321 for (Iterator i = bound.values().iterator(); i.hasNext();) { 322 for (Iterator j = ((Vector) i.next()).iterator(); j.hasNext();) 323 { 324 ((XPropertyChangeListener) j.next()).disposing(event); 325 } 326 } 327 } 328 if (veto != null) { 329 for (Iterator i = veto.values().iterator(); i.hasNext();) { 330 for (Iterator j = ((Vector) i.next()).iterator(); j.hasNext();) 331 { 332 ((XVetoableChangeListener) j.next()).disposing(event); 333 } 334 } 335 } 336 } 337 338 /** 339 Implements 340 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>. 341 */ 342 public XPropertySetInfo getPropertySetInfo() { 343 return new Info(properties); 344 } 345 346 /** 347 Implements <code>com.sun.star.beans.XPropertySet.setPropertyValue</code>. 348 */ 349 public void setPropertyValue(String propertyName, Object value) 350 throws UnknownPropertyException, PropertyVetoException, 351 com.sun.star.lang.IllegalArgumentException, WrappedTargetException 352 { 353 setProperty(propertyName, value, false, false, (short) 1); 354 } 355 356 /** 357 Implements <code>com.sun.star.beans.XPropertySet.getPropertyValue</code>. 358 */ 359 public Object getPropertyValue(String propertyName) 360 throws UnknownPropertyException, WrappedTargetException 361 { 362 return getProperty(propertyName, null); 363 } 364 365 /** 366 Implements 367 <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>. 368 369 <p>If a listener is added more than once, it will receive all relevant 370 notifications multiple times.</p> 371 */ 372 public void addPropertyChangeListener( 373 String propertyName, XPropertyChangeListener listener) 374 throws UnknownPropertyException, WrappedTargetException 375 { 376 // assert listener != null; 377 checkUnknown(propertyName); 378 boolean disp; 379 synchronized (this) { 380 disp = disposed; 381 if (!disp) { 382 Vector v = (Vector) boundListeners.get(propertyName); 383 if (v == null) { 384 v = new Vector(); 385 boundListeners.put(propertyName, v); 386 } 387 v.add(listener); 388 } 389 } 390 if (disp) { 391 listener.disposing(new EventObject(object)); 392 } 393 } 394 395 /** 396 Implements <code> 397 com.sun.star.beans.XPropertySet.removePropertyChangeListener</code>. 398 */ 399 public void removePropertyChangeListener( 400 String propertyName, XPropertyChangeListener listener) 401 throws UnknownPropertyException, WrappedTargetException 402 { 403 // assert listener != null; 404 checkUnknown(propertyName); 405 synchronized (this) { 406 if (boundListeners != null) { 407 Vector v = (Vector) boundListeners.get(propertyName); 408 if (v != null) { 409 v.remove(listener); 410 } 411 } 412 } 413 } 414 415 /** 416 Implements 417 <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>. 418 419 <p>If a listener is added more than once, it will receive all relevant 420 notifications multiple times.</p> 421 */ 422 public void addVetoableChangeListener( 423 String propertyName, XVetoableChangeListener listener) 424 throws UnknownPropertyException, WrappedTargetException 425 { 426 // assert listener != null; 427 checkUnknown(propertyName); 428 boolean disp; 429 synchronized (this) { 430 disp = disposed; 431 if (!disp) { 432 Vector v = (Vector) vetoListeners.get(propertyName); 433 if (v == null) { 434 v = new Vector(); 435 vetoListeners.put(propertyName, v); 436 } 437 v.add(listener); 438 } 439 } 440 if (disp) { 441 listener.disposing(new EventObject(object)); 442 } 443 } 444 445 /** 446 Implements <code> 447 com.sun.star.beans.XPropertySet.removeVetoableChangeListener</code>. 448 */ 449 public void removeVetoableChangeListener( 450 String propertyName, XVetoableChangeListener listener) 451 throws UnknownPropertyException, WrappedTargetException 452 { 453 // assert listener != null; 454 checkUnknown(propertyName); 455 synchronized (this) { 456 if (vetoListeners != null) { 457 Vector v = (Vector) vetoListeners.get(propertyName); 458 if (v != null) { 459 v.remove(listener); 460 } 461 } 462 } 463 } 464 465 /** 466 Implements 467 <code>com.sun.star.beans.XFastPropertySet.setFastPropertyValue</code>. 468 */ 469 public void setFastPropertyValue(int handle, Object value) 470 throws UnknownPropertyException, PropertyVetoException, 471 com.sun.star.lang.IllegalArgumentException, WrappedTargetException 472 { 473 setProperty(translateHandle(handle), value, false, false, (short) 1); 474 } 475 476 /** 477 Implements 478 <code>com.sun.star.beans.XFastPropertySet.getFastPropertyValue</code>. 479 */ 480 public Object getFastPropertyValue(int handle) 481 throws UnknownPropertyException, WrappedTargetException 482 { 483 return getProperty(translateHandle(handle), null); 484 } 485 486 /** 487 Implements 488 <code>com.sun.star.beans.XPropertyAccess.getPropertyValues</code>. 489 */ 490 public PropertyValue[] getPropertyValues() { 491 PropertyValue[] s = new PropertyValue[handleMap.length]; 492 int n = 0; 493 for (int i = 0; i < handleMap.length; ++i) { 494 PropertyState[] state = new PropertyState[1]; 495 Object value; 496 try { 497 value = getProperty(handleMap[i], state); 498 } catch (UnknownPropertyException e) { 499 continue; 500 } catch (WrappedTargetException e) { 501 throw new WrappedTargetRuntimeException( 502 e.getMessage(), object, e.TargetException); 503 } 504 s[n++] = new PropertyValue(handleMap[i], i, value, state[0]); 505 } 506 if (n < handleMap.length) { 507 PropertyValue[] s2 = new PropertyValue[n]; 508 System.arraycopy(s, 0, s2, 0, n); 509 s = s2; 510 } 511 return s; 512 } 513 514 /** 515 Implements 516 <code>com.sun.star.beans.XPropertyAccess.setPropertyValues</code>. 517 */ 518 public void setPropertyValues(PropertyValue[] props) 519 throws UnknownPropertyException, PropertyVetoException, 520 com.sun.star.lang.IllegalArgumentException, WrappedTargetException 521 { 522 for (int i = 0; i < props.length; ++i) { 523 if (props[i].Handle != -1 524 && !props[i].Name.equals(translateHandle(props[i].Handle))) 525 { 526 throw new UnknownPropertyException( 527 ("name " + props[i].Name + " does not match handle " 528 + props[i].Handle), 529 object); 530 } 531 setProperty( 532 props[i].Name, props[i].Value, 533 props[i].State == PropertyState.AMBIGUOUS_VALUE, 534 props[i].State == PropertyState.DEFAULT_VALUE, (short) 0); 535 } 536 } 537 538 /** 539 A class used by clients of {@link PropertySetMixin} when implementing UNO 540 interface type attribute setter functions. 541 542 @see #prepareSet(String, Object, Object, PropertySetMixin.BoundListeners) 543 */ 544 public static final class BoundListeners { 545 /** 546 The constructor. 547 */ 548 public BoundListeners() {} 549 550 /** 551 Notifies any 552 <code>com.sun.star.beans.XPropertyChangeListener</code>s. 553 554 @see #prepareSet(String, Object, Object, 555 PropertySetMixin.BoundListeners) 556 */ 557 public void notifyListeners() { 558 if (specificListeners != null) { 559 for (Iterator i = specificListeners.iterator(); i.hasNext();) { 560 try { 561 ((XPropertyChangeListener) i.next()).propertyChange( 562 event); 563 } catch (DisposedException e) {} 564 } 565 } 566 if (unspecificListeners != null) { 567 for (Iterator i = unspecificListeners.iterator(); i.hasNext();) 568 { 569 try { 570 ((XPropertyChangeListener) i.next()).propertyChange( 571 event); 572 } catch (DisposedException e) {} 573 } 574 } 575 } 576 577 private Vector specificListeners = null; 578 private Vector unspecificListeners = null; 579 private PropertyChangeEvent event = null; 580 } 581 582 private XIdlClass getReflection(String typeName) { 583 XIdlReflection refl; 584 try { 585 refl = UnoRuntime.queryInterface( 586 XIdlReflection.class, 587 context.getServiceManager().createInstanceWithContext( 588 "com.sun.star.reflection.CoreReflection", context)); 589 } catch (com.sun.star.uno.Exception e) { 590 throw new DeploymentException( 591 ("component context fails to supply service" 592 + " com.sun.star.reflection.CoreReflection: " 593 + e.getMessage()), 594 context); 595 } 596 try { 597 return refl.forName(typeName); 598 } finally { 599 XComponent comp = UnoRuntime.queryInterface(XComponent.class, refl); 600 if (comp != null) { 601 comp.dispose(); 602 } 603 } 604 } 605 606 private void initProperties( 607 XTypeDescription type, HashMap map, ArrayList handleNames, HashSet seen) 608 { 609 XInterfaceTypeDescription2 ifc = UnoRuntime.queryInterface( 610 XInterfaceTypeDescription2.class, resolveTypedefs(type)); 611 if (seen.add(ifc.getName())) { 612 XTypeDescription[] bases = ifc.getBaseTypes(); 613 for (int i = 0; i < bases.length; ++i) { 614 initProperties(bases[i], map, handleNames, seen); 615 } 616 XInterfaceMemberTypeDescription[] members = ifc.getMembers(); 617 for (int i = 0; i < members.length; ++i) { 618 if (members[i].getTypeClass() == TypeClass.INTERFACE_ATTRIBUTE) 619 { 620 XInterfaceAttributeTypeDescription2 attr = 621 UnoRuntime.queryInterface( 622 XInterfaceAttributeTypeDescription2.class, 623 members[i]); 624 short attrAttribs = 0; 625 if (attr.isBound()) { 626 attrAttribs |= PropertyAttribute.BOUND; 627 } 628 boolean setUnknown = false; 629 if (attr.isReadOnly()) { 630 attrAttribs |= PropertyAttribute.READONLY; 631 setUnknown = true; 632 } 633 XCompoundTypeDescription[] excs = attr.getGetExceptions(); 634 boolean getUnknown = false; 635 //XXX Special interpretation of getter/setter exceptions 636 // only works if the specified exceptions are of the exact 637 // type, not of a supertype: 638 for (int j = 0; j < excs.length; ++j) { 639 if (excs[j].getName().equals( 640 "com.sun.star.beans.UnknownPropertyException")) 641 { 642 getUnknown = true; 643 break; 644 } 645 } 646 excs = attr.getSetExceptions(); 647 for (int j = 0; j < excs.length; ++j) { 648 if (excs[j].getName().equals( 649 "com.sun.star.beans.UnknownPropertyException")) 650 { 651 setUnknown = true; 652 } else if (excs[j].getName().equals( 653 "com.sun.star.beans." 654 + "PropertyVetoException")) 655 { 656 attrAttribs |= PropertyAttribute.CONSTRAINED; 657 } 658 } 659 if (getUnknown && setUnknown) { 660 attrAttribs |= PropertyAttribute.OPTIONAL; 661 } 662 XTypeDescription t = attr.getType(); 663 for (;;) { 664 t = resolveTypedefs(t); 665 short n; 666 if (t.getName().startsWith( 667 "com.sun.star.beans.Ambiguous<")) 668 { 669 n = PropertyAttribute.MAYBEAMBIGUOUS; 670 } else if (t.getName().startsWith( 671 "com.sun.star.beans.Defaulted<")) 672 { 673 n = PropertyAttribute.MAYBEDEFAULT; 674 } else if (t.getName().startsWith( 675 "com.sun.star.beans.Optional<")) 676 { 677 n = PropertyAttribute.MAYBEVOID; 678 } else { 679 break; 680 } 681 attrAttribs |= n; 682 t = (UnoRuntime.queryInterface( 683 XStructTypeDescription.class, t)). 684 getTypeArguments()[0]; 685 } 686 String name = members[i].getMemberName(); 687 boolean present = true; 688 if (absentOptional != null) { 689 for (int j = 0; j < absentOptional.length; ++j) { 690 if (name.equals(absentOptional[j])) { 691 present = false; 692 break; 693 } 694 } 695 } 696 if (map.put( 697 name, 698 new PropertyData( 699 new Property( 700 name, handleNames.size(), 701 new Type(t.getName(), t.getTypeClass()), 702 attrAttribs), 703 present)) 704 != null) 705 { 706 throw new RuntimeException( 707 "inconsistent UNO type registry"); 708 } 709 handleNames.add(name); 710 } 711 } 712 } 713 } 714 715 private String translateHandle(int handle) throws UnknownPropertyException { 716 if (handle < 0 || handle >= handleMap.length) { 717 throw new UnknownPropertyException("bad handle " + handle, object); 718 } 719 return handleMap[handle]; 720 } 721 722 private void setProperty( 723 String name, Object value, boolean isAmbiguous, boolean isDefaulted, 724 short illegalArgumentPosition) 725 throws UnknownPropertyException, PropertyVetoException, 726 com.sun.star.lang.IllegalArgumentException, WrappedTargetException 727 { 728 PropertyData p = (PropertyData) properties.get(name); 729 if (p == null) { 730 throw new UnknownPropertyException(name, object); 731 } 732 if ((isAmbiguous 733 && (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) == 0) 734 || (isDefaulted 735 && ((p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) 736 == 0))) 737 { 738 throw new com.sun.star.lang.IllegalArgumentException( 739 ("flagging as ambiguous/defaulted non-ambiguous/defaulted" 740 + " property " + name), 741 object, illegalArgumentPosition); 742 743 } 744 XIdlField2 f = UnoRuntime.queryInterface( 745 XIdlField2.class, idlClass.getField(name)); 746 Object[] o = new Object[] { 747 new Any(type, UnoRuntime.queryInterface(type, object)) }; 748 Object v = wrapValue( 749 value, 750 UnoRuntime.queryInterface( 751 XIdlField2.class, idlClass.getField(name)).getType(), 752 (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0, 753 isAmbiguous, 754 (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0, 755 isDefaulted, 756 (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0); 757 try { 758 f.set(o, v); 759 } catch (com.sun.star.lang.IllegalArgumentException e) { 760 if (e.ArgumentPosition == 1) { 761 throw new com.sun.star.lang.IllegalArgumentException( 762 e.getMessage(), object, illegalArgumentPosition); 763 } else { 764 throw new RuntimeException( 765 "unexpected com.sun.star.lang.IllegalArgumentException: " 766 + e.getMessage()); 767 } 768 } catch (com.sun.star.lang.IllegalAccessException e) { 769 //TODO Clarify whether PropertyVetoException is the correct 770 // exception to throw when trying to set a read-only property: 771 throw new PropertyVetoException( 772 "cannot set read-only property " + name, object); 773 } catch (WrappedTargetRuntimeException e) { 774 //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not 775 // guaranteed to originate directly within XIdlField2.get (and thus 776 // have the expected semantics); it might also be passed through 777 // from lower layers. 778 if (new Type(UnknownPropertyException.class).isSupertypeOf( 779 AnyConverter.getType(e.TargetException)) 780 && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) 781 { 782 throw new UnknownPropertyException(name, object); 783 } else if (new Type(PropertyVetoException.class).isSupertypeOf( 784 AnyConverter.getType(e.TargetException)) 785 && ((p.property.Attributes 786 & PropertyAttribute.CONSTRAINED) 787 != 0)) 788 { 789 throw new PropertyVetoException(name, object); 790 } else { 791 throw new WrappedTargetException( 792 e.getMessage(), object, e.TargetException); 793 } 794 } 795 } 796 797 Object getProperty(String name, PropertyState[] state) 798 throws UnknownPropertyException, WrappedTargetException 799 { 800 PropertyData p = (PropertyData) properties.get(name); 801 if (p == null) { 802 throw new UnknownPropertyException(name, object); 803 } 804 XIdlField2 field = UnoRuntime.queryInterface( 805 XIdlField2.class, idlClass.getField(name)); 806 Object value; 807 try { 808 value = field.get( 809 new Any(type, UnoRuntime.queryInterface(type, object))); 810 } catch (com.sun.star.lang.IllegalArgumentException e) { 811 throw new RuntimeException( 812 "unexpected com.sun.star.lang.IllegalArgumentException: " 813 + e.getMessage()); 814 } catch (WrappedTargetRuntimeException e) { 815 //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not 816 // guaranteed to originate directly within XIdlField2.get (and thus 817 // have the expected semantics); it might also be passed through 818 // from lower layers. 819 if (new Type(UnknownPropertyException.class).isSupertypeOf( 820 AnyConverter.getType(e.TargetException)) 821 && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0) 822 { 823 throw new UnknownPropertyException(name, object); 824 } else { 825 throw new WrappedTargetException( 826 e.getMessage(), object, e.TargetException); 827 } 828 } 829 boolean undoAmbiguous 830 = (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0; 831 boolean undoDefaulted 832 = (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0; 833 boolean undoOptional 834 = (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0; 835 boolean isAmbiguous = false; 836 boolean isDefaulted = false; 837 while (undoAmbiguous || undoDefaulted || undoOptional) { 838 String typeName = AnyConverter.getType(value).getTypeName(); 839 if (undoAmbiguous 840 && typeName.startsWith("com.sun.star.beans.Ambiguous<")) 841 { 842 XIdlClass ambiguous = getReflection(typeName); 843 try { 844 isAmbiguous = AnyConverter.toBoolean( 845 UnoRuntime.queryInterface( 846 XIdlField2.class, 847 ambiguous.getField("IsAmbiguous")).get(value)); 848 value = UnoRuntime.queryInterface( 849 XIdlField2.class, 850 ambiguous.getField("Value")).get(value); 851 } catch (com.sun.star.lang.IllegalArgumentException e) { 852 throw new RuntimeException( 853 "unexpected" 854 + " com.sun.star.lang.IllegalArgumentException: " 855 + e.getMessage()); 856 } 857 undoAmbiguous = false; 858 } else if (undoDefaulted 859 && typeName.startsWith("com.sun.star.beans.Defaulted<")) 860 { 861 XIdlClass defaulted = getReflection(typeName); 862 try { 863 isDefaulted = AnyConverter.toBoolean( 864 UnoRuntime.queryInterface( 865 XIdlField2.class, 866 defaulted.getField("IsDefaulted")).get(value)); 867 value = UnoRuntime.queryInterface( 868 XIdlField2.class, 869 defaulted.getField("Value")).get(value); 870 } catch (com.sun.star.lang.IllegalArgumentException e) { 871 throw new RuntimeException( 872 "unexpected" 873 + " com.sun.star.lang.IllegalArgumentException: " 874 + e.getMessage()); 875 } 876 undoDefaulted = false; 877 } else if (undoOptional 878 && typeName.startsWith("com.sun.star.beans.Optional<")) 879 { 880 XIdlClass optional = getReflection(typeName); 881 try { 882 boolean present = AnyConverter.toBoolean( 883 UnoRuntime.queryInterface( 884 XIdlField2.class, 885 optional.getField("IsPresent")).get(value)); 886 if (!present) { 887 value = Any.VOID; 888 break; 889 } 890 value = UnoRuntime.queryInterface( 891 XIdlField2.class, 892 optional.getField("Value")).get(value); 893 } catch (com.sun.star.lang.IllegalArgumentException e) { 894 throw new RuntimeException( 895 "unexpected" 896 + " com.sun.star.lang.IllegalArgumentException: " 897 + e.getMessage()); 898 } 899 undoOptional = false; 900 } else { 901 throw new RuntimeException( 902 "unexpected type of attribute " + name); 903 } 904 } 905 if (state != null) { 906 //XXX If isAmbiguous && isDefaulted, arbitrarily choose 907 // AMBIGUOUS_VALUE over DEFAULT_VALUE: 908 state[0] = isAmbiguous 909 ? PropertyState.AMBIGUOUS_VALUE 910 : isDefaulted 911 ? PropertyState.DEFAULT_VALUE : PropertyState.DIRECT_VALUE; 912 } 913 return value; 914 } 915 916 private Object wrapValue( 917 Object value, XIdlClass type, boolean wrapAmbiguous, 918 boolean isAmbiguous, boolean wrapDefaulted, boolean isDefaulted, 919 boolean wrapOptional) 920 { 921 // assert (wrapAmbiguous || !isAmbiguous) 922 // && (wrapDefaulted || !isDefaulted); 923 if (wrapAmbiguous 924 && type.getName().startsWith("com.sun.star.beans.Ambiguous<")) 925 { 926 Object[] strct = new Object[1]; 927 type.createObject(strct); 928 try { 929 XIdlField2 field = UnoRuntime.queryInterface( 930 XIdlField2.class, type.getField("Value")); 931 field.set( 932 strct, 933 wrapValue( 934 value, field.getType(), false, false, wrapDefaulted, 935 isDefaulted, wrapOptional)); 936 UnoRuntime.queryInterface( 937 XIdlField2.class, type.getField("IsAmbiguous")).set( 938 strct, new Boolean(isAmbiguous)); 939 } catch (com.sun.star.lang.IllegalArgumentException e) { 940 throw new RuntimeException( 941 "unexpected com.sun.star.lang.IllegalArgumentException: " 942 + e.getMessage()); 943 } catch (com.sun.star.lang.IllegalAccessException e) { 944 throw new RuntimeException( 945 "unexpected com.sun.star.lang.IllegalAccessException: " 946 + e.getMessage()); 947 } 948 return strct[0]; 949 } else if (wrapDefaulted 950 && type.getName().startsWith( 951 "com.sun.star.beans.Defaulted<")) 952 { 953 Object[] strct = new Object[1]; 954 type.createObject(strct); 955 try { 956 XIdlField2 field = UnoRuntime.queryInterface( 957 XIdlField2.class, type.getField("Value")); 958 field.set( 959 strct, 960 wrapValue( 961 value, field.getType(), wrapAmbiguous, isAmbiguous, 962 false, false, wrapOptional)); 963 UnoRuntime.queryInterface( 964 XIdlField2.class, type.getField("IsDefaulted")).set( 965 strct, new Boolean(isDefaulted)); 966 } catch (com.sun.star.lang.IllegalArgumentException e) { 967 throw new RuntimeException( 968 "unexpected com.sun.star.lang.IllegalArgumentException: " 969 + e.getMessage()); 970 } catch (com.sun.star.lang.IllegalAccessException e) { 971 throw new RuntimeException( 972 "unexpected com.sun.star.lang.IllegalAccessException: " 973 + e.getMessage()); 974 } 975 return strct[0]; 976 } else if (wrapOptional 977 && type.getName().startsWith("com.sun.star.beans.Optional<")) 978 { 979 Object[] strct = new Object[1]; 980 type.createObject(strct); 981 boolean present = !AnyConverter.isVoid(value); 982 try { 983 UnoRuntime.queryInterface( 984 XIdlField2.class, type.getField("IsPresent")).set( 985 strct, new Boolean(present)); 986 if (present) { 987 XIdlField2 field = UnoRuntime.queryInterface( 988 XIdlField2.class, type.getField("Value")); 989 field.set( 990 strct, 991 wrapValue( 992 value, field.getType(), wrapAmbiguous, isAmbiguous, 993 wrapDefaulted, isDefaulted, false)); 994 } 995 } catch (com.sun.star.lang.IllegalArgumentException e) { 996 throw new RuntimeException( 997 "unexpected com.sun.star.lang.IllegalArgumentException: " 998 + e.getMessage()); 999 } catch (com.sun.star.lang.IllegalAccessException e) { 1000 throw new RuntimeException( 1001 "unexpected com.sun.star.lang.IllegalAccessException: " 1002 + e.getMessage()); 1003 } 1004 return strct[0]; 1005 } else { 1006 if (wrapAmbiguous || wrapDefaulted || wrapOptional) { 1007 throw new RuntimeException("unexpected type of attribute"); 1008 } 1009 return value; 1010 } 1011 } 1012 1013 private static XTypeDescription resolveTypedefs(XTypeDescription type) { 1014 while (type.getTypeClass() == TypeClass.TYPEDEF) { 1015 type = UnoRuntime.queryInterface( 1016 XIndirectTypeDescription.class, type).getReferencedType(); 1017 } 1018 return type; 1019 } 1020 1021 private PropertyData get(Object object, String propertyName) 1022 throws UnknownPropertyException 1023 { 1024 PropertyData p = (PropertyData) properties.get(propertyName); 1025 if (p == null || !p.present) { 1026 throw new UnknownPropertyException(propertyName, object); 1027 } 1028 return p; 1029 } 1030 1031 private void checkUnknown(String propertyName) 1032 throws UnknownPropertyException 1033 { 1034 if (!propertyName.equals("")) { 1035 get(this, propertyName); 1036 } 1037 } 1038 1039 private static final class PropertyData { 1040 public PropertyData(Property property, boolean present) { 1041 this.property = property; 1042 this.present = present; 1043 } 1044 1045 public final Property property; 1046 public final boolean present; 1047 } 1048 1049 private final class Info extends WeakBase implements XPropertySetInfo 1050 { 1051 public Info(Map properties) { 1052 this.properties = properties; 1053 } 1054 1055 public Property[] getProperties() { 1056 ArrayList al = new ArrayList(properties.size()); 1057 for (Iterator i = properties.values().iterator(); i.hasNext();) { 1058 PropertyData p = (PropertyData) i.next(); 1059 if (p.present) { 1060 al.add(p.property); 1061 } 1062 } 1063 return (Property[]) al.toArray(new Property[al.size()]); 1064 } 1065 1066 public Property getPropertyByName(String name) 1067 throws UnknownPropertyException 1068 { 1069 return get(this, name).property; 1070 } 1071 1072 public boolean hasPropertyByName(String name) { 1073 PropertyData p = (PropertyData) properties.get(name); 1074 return p != null && p.present; 1075 } 1076 1077 private final Map properties; // from String to Property 1078 } 1079 1080 private final XComponentContext context; 1081 private final XInterface object; 1082 private final Type type; 1083 private final String[] absentOptional; 1084 private final XIdlClass idlClass; 1085 private final Map properties; // from String to Property 1086 private final String[] handleMap; 1087 1088 private HashMap boundListeners = new HashMap(); 1089 // from String to Vector of XPropertyChangeListener 1090 private HashMap vetoListeners = new HashMap(); 1091 // from String to Vector of XVetoableChangeListener 1092 private boolean disposed = false; 1093 } 1094