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