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