uno grid a11y: Use ImplInheritanceHelper
[LibreOffice.git] / ridljar / com / sun / star / lib / uno / helper / PropertySetMixin.java
blob0c050d676928c0c0d25ef58b5200359261fec8f5
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 package com.sun.star.lib.uno.helper;
21 import com.sun.star.beans.Property;
22 import com.sun.star.beans.PropertyAttribute;
23 import com.sun.star.beans.PropertyChangeEvent;
24 import com.sun.star.beans.PropertyState;
25 import com.sun.star.beans.PropertyValue;
26 import com.sun.star.beans.PropertyVetoException;
27 import com.sun.star.beans.UnknownPropertyException;
28 import com.sun.star.beans.XPropertyChangeListener;
29 import com.sun.star.beans.XPropertySetInfo;
30 import com.sun.star.beans.XVetoableChangeListener;
31 import com.sun.star.container.NoSuchElementException;
32 import com.sun.star.container.XHierarchicalNameAccess;
33 import com.sun.star.lang.DisposedException;
34 import com.sun.star.lang.EventObject;
35 import com.sun.star.lang.WrappedTargetException;
36 import com.sun.star.lang.WrappedTargetRuntimeException;
37 import com.sun.star.reflection.XCompoundTypeDescription;
38 import com.sun.star.reflection.XIdlClass;
39 import com.sun.star.reflection.XIdlField2;
40 import com.sun.star.reflection.XIndirectTypeDescription;
41 import com.sun.star.reflection.XInterfaceAttributeTypeDescription2;
42 import com.sun.star.reflection.XInterfaceMemberTypeDescription;
43 import com.sun.star.reflection.XInterfaceTypeDescription2;
44 import com.sun.star.reflection.XStructTypeDescription;
45 import com.sun.star.reflection.XTypeDescription;
46 import com.sun.star.reflection.theCoreReflection;
47 import com.sun.star.uno.Any;
48 import com.sun.star.uno.AnyConverter;
49 import com.sun.star.uno.Type;
50 import com.sun.star.uno.TypeClass;
51 import com.sun.star.uno.UnoRuntime;
52 import com.sun.star.uno.XComponentContext;
53 import com.sun.star.uno.XInterface;
54 import java.util.ArrayList;
55 import java.util.HashMap;
56 import java.util.HashSet;
57 import java.util.Iterator;
58 import java.util.Map;
60 /**
61 A helper mixin to implement certain UNO interfaces related to property set
62 handling on top of the attributes of a given UNO interface type.
64 <p>A client will mix in this class by keeping a reference to an instance of
65 this class, and forwarding all methods of (a subset of the interfaces)
66 <code>com.sun.star.beans.XPropertySet</code>,
67 <code>com.sun.star.beans.XFastPropertySet</code>, and
68 <code>com.sun.star.beans.XPropertyAccess</code> to it.</p>
70 <p>Client code should not use the monitors associated with instances of this
71 class, as they are used for internal purposes.</p>
73 @since UDK 3.2
75 public final class PropertySetMixin {
76 /**
77 The constructor.
79 @param context the component context used by this instance; must not be
80 null, and must supply the
81 <code>com.sun.star.reflection.theCoreReflection</code> and
82 <code>com.sun.star.reflection.theTypeDescriptionManager</code> singletons
84 @param object the client UNO object into which this instance is mixed in;
85 must not be null, and must support the given <code>type</code>
87 @param type the UNO interface type whose attributes are mapped to
88 properties; must not be null, and must represent a UNO interface type
90 @param absentOptional a list of optional properties that are not present,
91 and should thus not be visible via
92 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>,
93 <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>,
94 <code>com.sun.star.beans.XPropertySet.removePropertyChangeListener<!--
95 --></code>,
96 <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>,
97 and <code>com.sun.star.beans.XPropertySet.<!--
98 -->removeVetoableChangeListener</code>; null is treated the same as an
99 empty list; if non-null, the given array must not be modified after it is
100 passed to this constructor. For consistency reasons, the given
101 <code>absentOptional</code> should only contain the names of attributes
102 that represent optional properties that are not present (that is, the
103 attribute getters and setters always throw a
104 <code>com.sun.star.beans.UnknownPropertyException</code>), and should
105 contain each such name only once. If an optional property is not present
106 (that is, the corresponding attribute getter and setter always throw a
107 <code>com.sun.star.beans.UnknownPropertyException</code>) but is not
108 contained in the given <code>absentOptional</code>, then it will be
109 visible via
110 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code> as a
111 <code>com.sun.star.beans.Property</code> with a set
112 <code>com.sun.star.beans.PropertyAttribute.OPTIONAL</code>. If the given
113 <code>object</code> does not implement
114 <code>com.sun.star.beans.XPropertySet</code>, then the given
115 <code>absentOptional</code> is effectively ignored and can be null or
116 empty.
118 public PropertySetMixin(
119 XComponentContext context, XInterface object, Type type,
120 String[] absentOptional)
122 this.context = context;
123 this.object = object;
124 this.type = type;
125 this.absentOptional = absentOptional;
126 idlClass = getReflection(type.getTypeName());
127 XTypeDescription ifc;
128 try {
129 XHierarchicalNameAccess xhna = UnoRuntime.queryInterface(
130 XHierarchicalNameAccess.class,
131 context.getValueByName(
132 "/singletons/com.sun.star.reflection."
133 + "theTypeDescriptionManager"));
134 ifc = UnoRuntime.queryInterface(
135 XTypeDescription.class,
136 xhna.getByHierarchicalName(type.getTypeName()));
137 } catch (NoSuchElementException e) {
138 throw new RuntimeException(e);
140 HashMap<String,PropertyData> map = new HashMap<String,PropertyData>();
141 ArrayList<String> handleNames = new ArrayList<String>();
142 initProperties(ifc, map, handleNames, new HashSet<String>());
143 properties = map;
144 handleMap = handleNames.toArray(new String[handleNames.size()]);
148 A method used by clients when implementing UNO interface type attribute
149 setter functions.
151 <p>First, this method checks whether this instance has already been
152 disposed (see {@link #dispose}), and throws a
153 <code>com.sun.star.beans.DisposedException</code> if applicable. For a
154 constrained attribute (whose setter can explicitly raise
155 <code>com.sun.star.beans.PropertyVetoException</code>), this method
156 notifies any <code>com.sun.star.beans.XVetoableChangeListener</code>s.
157 For a bound attribute, this method modifies the passed-in
158 <code>bound</code> so that it can afterwards be used to notify any
159 <code>com.sun.star.beans.XPropertyChangeListener</code>s. This method
160 should be called before storing the new attribute value, and
161 <code>bound.notifyListeners()</code> should be called exactly once after
162 storing the new attribute value (in case the attribute is bound;
163 otherwise, calling <code>bound.notifyListeners()</code> is ignored).
164 Furthermore, <code>bound.notifyListeners()</code> and this method have to
165 be called from the same thread.</p>
167 @param propertyName the name of the property (which is the same as the
168 name of the attribute that is going to be set)
170 @param oldValue the property value corresponding to the old attribute
171 value. This is only used as
172 <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code>, which is
173 rather useless, anyway (see &ldquo;Using the Observer Pattern&rdquo; in
174 <a href="http://tools.openoffice.org/CodingGuidelines.sxw">
175 <cite>OpenOffice.org Coding Guidelines</cite></a>). If the attribute
176 that is going to be set is neither bound nor constrained, or if
177 <code>com.sun.star.beans.PropertyChangeEvent.OldValue</code> should not
178 be set, {@link Any#VOID} can be used instead.
180 @param newValue the property value corresponding to the new
181 attribute value. This is only used as
182 <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code>, which is
183 rather useless, anyway (see &ldquo;Using the Observer Pattern&rdquo; in
184 <a href="http://tools.openoffice.org/CodingGuidelines.sxw">
185 <cite>OpenOffice.org Coding Guidelines</cite></a>), <em>unless</em> the
186 attribute that is going to be set is constrained. If the attribute
187 that is going to be set is neither bound nor constrained, or if it is
188 only bound but
189 <code>com.sun.star.beans.PropertyChangeEvent.NewValue</code> should not
190 be set, {@link Any#VOID} can be used instead.
192 @param bound a reference to a fresh {@link BoundListeners} instance
193 (which has not been passed to this method before, and on which
194 {@link BoundListeners#notifyListeners} has not yet been called); may only
195 be null if the attribute that is going to be set is not bound
197 @throws PropertyVetoException if a vetoable listener throws it.
199 public void prepareSet(
200 String propertyName, Object oldValue, Object newValue,
201 BoundListeners bound)
202 throws PropertyVetoException
204 // assert properties.get(propertyName) != null;
205 Property p = properties.get(propertyName).property;
206 ArrayList<XVetoableChangeListener> specificVeto = null;
207 ArrayList<XVetoableChangeListener> unspecificVeto = null;
208 synchronized (this) {
209 if (disposed) {
210 throw new DisposedException("disposed", object);
212 if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) {
213 ArrayList<XVetoableChangeListener> o = vetoListeners.get(propertyName);
214 if (o != null) {
215 specificVeto = new ArrayList<XVetoableChangeListener>(o);
217 o = vetoListeners.get("");
218 if (o != null) {
219 unspecificVeto = new ArrayList<XVetoableChangeListener>(o);
222 if ((p.Attributes & PropertyAttribute.BOUND) != 0) {
223 // assert bound != null;
224 ArrayList<XPropertyChangeListener> o = boundListeners.get(propertyName);
225 if (o != null) {
226 bound.specificListeners = new ArrayList<XPropertyChangeListener>(o);
228 o = boundListeners.get("");
229 if (o != null) {
230 bound.unspecificListeners = new ArrayList<XPropertyChangeListener>(o);
234 if ((p.Attributes & PropertyAttribute.CONSTRAINED) != 0) {
235 PropertyChangeEvent event = new PropertyChangeEvent(
236 object, propertyName, false, p.Handle, oldValue, newValue);
237 if (specificVeto != null) {
238 for (Iterator<XVetoableChangeListener> i = specificVeto.iterator(); i.hasNext();) {
239 try {
240 i.next().vetoableChange(event);
241 } catch (DisposedException e) {}
244 if (unspecificVeto != null) {
245 for (Iterator<XVetoableChangeListener> i = unspecificVeto.iterator(); i.hasNext();) {
246 try {
247 i.next().vetoableChange(event);
248 } catch (DisposedException e) {}
252 if ((p.Attributes & PropertyAttribute.BOUND) != 0) {
253 // assert bound != null;
254 bound.event = new PropertyChangeEvent(
255 object, propertyName, false, p.Handle, oldValue, newValue);
260 A simplified version of {@link #prepareSet(String, Object, Object,
261 PropertySetMixin.BoundListeners)}.
263 <p>This method is useful for attributes that are not constrained.</p>
265 @param propertyName the name of the property (which is the same as the
266 name of the attribute that is going to be set)
268 @param bound a reference to a fresh {@link BoundListeners} instance
269 (which has not been passed to this method before, and on which
270 {@link BoundListeners#notifyListeners} has not yet been called); may only
271 be null if the attribute that is going to be set is not bound
273 public void prepareSet(String propertyName, BoundListeners bound) {
274 try {
275 prepareSet(propertyName, Any.VOID, Any.VOID, bound);
276 } catch (PropertyVetoException e) {
277 throw new RuntimeException("unexpected " + e);
282 Marks this instance as being disposed.
284 <p>See <code>com.sun.star.lang.XComponent</code> for the general concept
285 of disposing UNO objects. On the first call to this method, all
286 registered listeners
287 (<code>com.sun.star.beans.XPropertyChangeListener</code>s and
288 <code>com.sun.star.beans.XVetoableChangeListener</code>s) are notified of
289 the disposing source. Any subsequent calls to this method are
290 ignored.</p>
292 public void dispose() {
293 HashMap<String,ArrayList<XPropertyChangeListener>> bound;
294 HashMap<String,ArrayList<XVetoableChangeListener>> veto;
295 synchronized (this) {
296 bound = boundListeners;
297 boundListeners = null;
298 veto = vetoListeners;
299 vetoListeners = null;
300 disposed = true;
302 EventObject event = new EventObject(object);
303 if (bound != null) {
304 for (Iterator<ArrayList<XPropertyChangeListener>> i = bound.values().iterator(); i.hasNext();) {
305 for (Iterator<XPropertyChangeListener> j = i.next().iterator(); j.hasNext();)
307 j.next().disposing(event);
311 if (veto != null) {
312 for (Iterator<ArrayList<XVetoableChangeListener>> i = veto.values().iterator(); i.hasNext();) {
313 for (Iterator<XVetoableChangeListener> j = i.next().iterator(); j.hasNext();)
315 j.next().disposing(event);
322 Implements
323 <code>com.sun.star.beans.XPropertySet.getPropertySetInfo</code>.
324 @return See com.sun.star.beans.XPropertySet
326 public XPropertySetInfo getPropertySetInfo() {
327 return new Info(properties);
331 Implements <code>com.sun.star.beans.XPropertySet.setPropertyValue</code>.
332 @param propertyName
333 See com.sun.star.beans.XPropertySet
334 @param value
335 See com.sun.star.beans.XPropertySet
336 @throws UnknownPropertyException
337 See com.sun.star.beans.XPropertySet
338 @throws PropertyVetoException
339 See com.sun.star.beans.XPropertySet
340 @throws WrappedTargetException
341 See com.sun.star.beans.XPropertySet
343 public void setPropertyValue(String propertyName, Object value)
344 throws UnknownPropertyException, PropertyVetoException,
345 com.sun.star.lang.IllegalArgumentException, WrappedTargetException
347 setProperty(propertyName, value, false, false, (short) 1);
351 Implements <code>com.sun.star.beans.XPropertySet.getPropertyValue</code>.
352 @param propertyName
353 See com.sun.star.beans.XPropertySet
354 @throws UnknownPropertyException
355 See com.sun.star.beans.XPropertySet
356 @throws WrappedTargetException
357 See com.sun.star.beans.XPropertySet
358 @return
359 See com.sun.star.beans.XPropertySet
361 public Object getPropertyValue(String propertyName)
362 throws UnknownPropertyException, WrappedTargetException
364 return getProperty(propertyName, null);
368 Implements
369 <code>com.sun.star.beans.XPropertySet.addPropertyChangeListener</code>.
371 <p>If a listener is added more than once, it will receive all relevant
372 notifications multiple times.</p>
374 @param propertyName
375 See com.sun.star.beans.XPropertySet
376 @param listener
377 See com.sun.star.beans.XPropertySet
378 @throws UnknownPropertyException
379 See com.sun.star.beans.XPropertySet
380 @throws WrappedTargetException
381 See com.sun.star.beans.XPropertySet
383 public void addPropertyChangeListener(
384 String propertyName, XPropertyChangeListener listener)
385 throws UnknownPropertyException, WrappedTargetException
387 // assert listener != null;
388 checkUnknown(propertyName);
389 boolean disp;
390 synchronized (this) {
391 disp = disposed;
392 if (!disp) {
393 ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName);
394 if (v == null) {
395 v = new ArrayList<XPropertyChangeListener>();
396 boundListeners.put(propertyName, v);
398 v.add(listener);
401 if (disp) {
402 listener.disposing(new EventObject(object));
407 Implements <code>
408 com.sun.star.beans.XPropertySet.removePropertyChangeListener</code>.
410 @param propertyName
411 See com.sun.star.beans.XPropertySet
412 @param listener
413 See com.sun.star.beans.XPropertySet
414 @throws UnknownPropertyException
415 See com.sun.star.beans.XPropertySet
416 @throws WrappedTargetException
417 See com.sun.star.beans.XPropertySet
419 public void removePropertyChangeListener(
420 String propertyName, XPropertyChangeListener listener)
421 throws UnknownPropertyException, WrappedTargetException
423 // assert listener != null;
424 checkUnknown(propertyName);
425 synchronized (this) {
426 if (boundListeners != null) {
427 ArrayList<XPropertyChangeListener> v = boundListeners.get(propertyName);
428 if (v != null) {
429 v.remove(listener);
436 Implements
437 <code>com.sun.star.beans.XPropertySet.addVetoableChangeListener</code>.
439 <p>If a listener is added more than once, it will receive all relevant
440 notifications multiple times.</p>
442 @param propertyName
443 See com.sun.star.beans.XPropertySet
444 @param listener
445 See com.sun.star.beans.XPropertySet
446 @throws UnknownPropertyException
447 See com.sun.star.beans.XPropertySet
448 @throws WrappedTargetException
449 See com.sun.star.beans.XPropertySet
451 public void addVetoableChangeListener(
452 String propertyName, XVetoableChangeListener listener)
453 throws UnknownPropertyException, WrappedTargetException
455 // assert listener != null;
456 checkUnknown(propertyName);
457 boolean disp;
458 synchronized (this) {
459 disp = disposed;
460 if (!disp) {
461 ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName);
462 if (v == null) {
463 v = new ArrayList<XVetoableChangeListener>();
464 vetoListeners.put(propertyName, v);
466 v.add(listener);
469 if (disp) {
470 listener.disposing(new EventObject(object));
475 Implements <code>
476 com.sun.star.beans.XPropertySet.removeVetoableChangeListener</code>.
478 @param propertyName
479 See com.sun.star.beans.XPropertySet
480 @param listener
481 See com.sun.star.beans.XPropertySet
482 @throws UnknownPropertyException
483 See com.sun.star.beans.XPropertySet
484 @throws WrappedTargetException
485 See com.sun.star.beans.XPropertySet
487 public void removeVetoableChangeListener(
488 String propertyName, XVetoableChangeListener listener)
489 throws UnknownPropertyException, WrappedTargetException
491 // assert listener != null;
492 checkUnknown(propertyName);
493 synchronized (this) {
494 if (vetoListeners != null) {
495 ArrayList<XVetoableChangeListener> v = vetoListeners.get(propertyName);
496 if (v != null) {
497 v.remove(listener);
504 Implements
505 <code>com.sun.star.beans.XFastPropertySet.setFastPropertyValue</code>.
507 @param handle
508 See com.sun.star.beans.XFastPropertySet
509 @param value
510 See com.sun.star.beans.XFastPropertySet
511 @throws UnknownPropertyException
512 See com.sun.star.beans.XFastPropertySet
513 @throws PropertyVetoException
514 See com.sun.star.beans.XFastPropertySet
515 @throws WrappedTargetException
516 See com.sun.star.beans.XFastPropertySet
518 public void setFastPropertyValue(int handle, Object value)
519 throws UnknownPropertyException, PropertyVetoException,
520 com.sun.star.lang.IllegalArgumentException, WrappedTargetException
522 setProperty(translateHandle(handle), value, false, false, (short) 1);
526 Implements
527 <code>com.sun.star.beans.XFastPropertySet.getFastPropertyValue</code>.
529 @param handle
530 See com.sun.star.beans.XFastPropertySet
531 @throws UnknownPropertyException
532 See com.sun.star.beans.XFastPropertySet
533 @throws WrappedTargetException
534 See com.sun.star.beans.XFastPropertySet
535 @return
536 See com.sun.star.beans.XFastPropertySet
538 public Object getFastPropertyValue(int handle)
539 throws UnknownPropertyException, WrappedTargetException
541 return getProperty(translateHandle(handle), null);
545 Implements
546 <code>com.sun.star.beans.XPropertyAccess.getPropertyValues</code>.
548 @return
549 See com.sun.star.beans.XPropertyAccess
551 public PropertyValue[] getPropertyValues() {
552 PropertyValue[] s = new PropertyValue[handleMap.length];
553 int n = 0;
554 for (int i = 0; i < handleMap.length; ++i) {
555 PropertyState[] state = new PropertyState[1];
556 Object value;
557 try {
558 value = getProperty(handleMap[i], state);
559 } catch (UnknownPropertyException e) {
560 continue;
561 } catch (WrappedTargetException e) {
562 throw new WrappedTargetRuntimeException(e.getCause(),
563 e.getMessage(), object, e.TargetException);
565 s[n++] = new PropertyValue(handleMap[i], i, value, state[0]);
567 if (n < handleMap.length) {
568 PropertyValue[] s2 = new PropertyValue[n];
569 System.arraycopy(s, 0, s2, 0, n);
570 s = s2;
572 return s;
576 Implements
577 <code>com.sun.star.beans.XPropertyAccess.setPropertyValues</code>.
579 @param props
580 See com.sun.star.beans.XPropertyAccess
581 @throws UnknownPropertyException
582 See com.sun.star.beans.XPropertyAccess
583 @throws PropertyVetoException
584 See com.sun.star.beans.XPropertyAccess
585 @throws WrappedTargetException
586 See com.sun.star.beans.XPropertyAccess
588 public void setPropertyValues(PropertyValue[] props)
589 throws UnknownPropertyException, PropertyVetoException,
590 com.sun.star.lang.IllegalArgumentException, WrappedTargetException
592 for (int i = 0; i < props.length; ++i) {
593 if (props[i].Handle != -1
594 && !props[i].Name.equals(translateHandle(props[i].Handle)))
596 throw new UnknownPropertyException(
597 ("name " + props[i].Name + " does not match handle "
598 + props[i].Handle),
599 object);
601 setProperty(
602 props[i].Name, props[i].Value,
603 props[i].State == PropertyState.AMBIGUOUS_VALUE,
604 props[i].State == PropertyState.DEFAULT_VALUE, (short) 0);
609 A class used by clients of {@link PropertySetMixin} when implementing UNO
610 interface type attribute setter functions.
612 @see #prepareSet(String, Object, Object, PropertySetMixin.BoundListeners)
614 public static final class BoundListeners {
617 Notifies any
618 <code>com.sun.star.beans.XPropertyChangeListener</code>s.
620 @see #prepareSet(String, Object, Object,
621 PropertySetMixin.BoundListeners)
623 public void notifyListeners() {
624 if (specificListeners != null) {
625 for (Iterator<XPropertyChangeListener> i = specificListeners.iterator(); i.hasNext();) {
626 try {
627 i.next().propertyChange(event);
628 } catch (DisposedException e) {}
631 if (unspecificListeners != null) {
632 for (Iterator<XPropertyChangeListener> i = unspecificListeners.iterator(); i.hasNext();)
634 try {
635 i.next().propertyChange(event);
636 } catch (DisposedException e) {}
641 private ArrayList<XPropertyChangeListener> specificListeners = null;
642 private ArrayList<XPropertyChangeListener> unspecificListeners = null;
643 private PropertyChangeEvent event = null;
646 private XIdlClass getReflection(String typeName) {
647 return theCoreReflection.get(context).forName(typeName);
650 private void initProperties(
651 XTypeDescription type, HashMap<String,PropertyData> map, ArrayList<String> handleNames, HashSet<String> seen)
653 XInterfaceTypeDescription2 ifc = UnoRuntime.queryInterface(
654 XInterfaceTypeDescription2.class, resolveTypedefs(type));
655 if (!seen.add(ifc.getName())) {
656 return;
658 XTypeDescription[] bases = ifc.getBaseTypes();
659 for (int i = 0; i < bases.length; ++i) {
660 initProperties(bases[i], map, handleNames, seen);
662 XInterfaceMemberTypeDescription[] members = ifc.getMembers();
663 for (int i = 0; i < members.length; ++i) {
664 if (members[i].getTypeClass() == TypeClass.INTERFACE_ATTRIBUTE)
666 XInterfaceAttributeTypeDescription2 attr =
667 UnoRuntime.queryInterface(
668 XInterfaceAttributeTypeDescription2.class,
669 members[i]);
670 short attrAttribs = 0;
671 if (attr.isBound()) {
672 attrAttribs |= PropertyAttribute.BOUND;
674 boolean setUnknown = false;
675 if (attr.isReadOnly()) {
676 attrAttribs |= PropertyAttribute.READONLY;
677 setUnknown = true;
679 XCompoundTypeDescription[] excs = attr.getGetExceptions();
680 boolean getUnknown = false;
681 //XXX Special interpretation of getter/setter exceptions
682 // only works if the specified exceptions are of the exact
683 // type, not of a supertype:
684 for (int j = 0; j < excs.length; ++j) {
685 if (excs[j].getName().equals(
686 "com.sun.star.beans.UnknownPropertyException"))
688 getUnknown = true;
689 break;
692 excs = attr.getSetExceptions();
693 for (int j = 0; j < excs.length; ++j) {
694 if (excs[j].getName().equals(
695 "com.sun.star.beans.UnknownPropertyException"))
697 setUnknown = true;
698 } else if (excs[j].getName().equals(
699 "com.sun.star.beans."
700 + "PropertyVetoException"))
702 attrAttribs |= PropertyAttribute.CONSTRAINED;
705 if (getUnknown && setUnknown) {
706 attrAttribs |= PropertyAttribute.OPTIONAL;
708 XTypeDescription t = attr.getType();
709 for (;;) {
710 t = resolveTypedefs(t);
711 short n;
712 if (t.getName().startsWith(
713 "com.sun.star.beans.Ambiguous<"))
715 n = PropertyAttribute.MAYBEAMBIGUOUS;
716 } else if (t.getName().startsWith(
717 "com.sun.star.beans.Defaulted<"))
719 n = PropertyAttribute.MAYBEDEFAULT;
720 } else if (t.getName().startsWith(
721 "com.sun.star.beans.Optional<"))
723 n = PropertyAttribute.MAYBEVOID;
724 } else {
725 break;
727 attrAttribs |= n;
728 t = UnoRuntime.queryInterface(XStructTypeDescription.class, t).getTypeArguments()[0];
730 String name = members[i].getMemberName();
731 boolean present = true;
732 if (absentOptional != null) {
733 for (int j = 0; j < absentOptional.length; ++j) {
734 if (name.equals(absentOptional[j])) {
735 present = false;
736 break;
740 if (map.put(
741 name,
742 new PropertyData(
743 new Property(
744 name, handleNames.size(),
745 new Type(t.getName(), t.getTypeClass()),
746 attrAttribs),
747 present))
748 != null)
750 throw new RuntimeException(
751 "inconsistent UNO type registry");
753 handleNames.add(name);
758 private String translateHandle(int handle) throws UnknownPropertyException {
759 if (handle < 0 || handle >= handleMap.length) {
760 throw new UnknownPropertyException("bad handle " + handle, object);
762 return handleMap[handle];
765 private void setProperty(
766 String name, Object value, boolean isAmbiguous, boolean isDefaulted,
767 short illegalArgumentPosition)
768 throws UnknownPropertyException, PropertyVetoException,
769 com.sun.star.lang.IllegalArgumentException, WrappedTargetException
771 PropertyData p = properties.get(name);
772 if (p == null) {
773 throw new UnknownPropertyException(name, object);
775 if ((isAmbiguous
776 && (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) == 0)
777 || (isDefaulted
778 && ((p.property.Attributes & PropertyAttribute.MAYBEDEFAULT)
779 == 0)))
781 throw new com.sun.star.lang.IllegalArgumentException(
782 ("flagging as ambiguous/defaulted non-ambiguous/defaulted"
783 + " property " + name),
784 object, illegalArgumentPosition);
787 XIdlField2 f = UnoRuntime.queryInterface(
788 XIdlField2.class, idlClass.getField(name));
789 Object[] o = new Object[] {
790 new Any(type, UnoRuntime.queryInterface(type, object)) };
791 Object v = wrapValue(
792 value,
793 UnoRuntime.queryInterface(
794 XIdlField2.class, idlClass.getField(name)).getType(),
795 (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0,
796 isAmbiguous,
797 (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0,
798 isDefaulted,
799 (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0);
800 try {
801 f.set(o, v);
802 } catch (com.sun.star.lang.IllegalArgumentException e) {
803 if (e.ArgumentPosition == 1) {
804 throw new com.sun.star.lang.IllegalArgumentException(e,
805 e.getMessage(), object, illegalArgumentPosition);
806 } else {
807 throw new RuntimeException(e);
809 } catch (com.sun.star.lang.IllegalAccessException e) {
810 //TODO Clarify whether PropertyVetoException is the correct
811 // exception to throw when trying to set a read-only property:
812 throw new PropertyVetoException(e,
813 "cannot set read-only property " + name, object);
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)
823 throw new UnknownPropertyException(e, name, object);
824 } else if (new Type(PropertyVetoException.class).isSupertypeOf(
825 AnyConverter.getType(e.TargetException))
826 && ((p.property.Attributes
827 & PropertyAttribute.CONSTRAINED)
828 != 0))
830 throw new PropertyVetoException(e, name, object);
831 } else {
832 throw new WrappedTargetException(e.getCause(),
833 e.getMessage(), object, e.TargetException);
838 Object getProperty(String name, PropertyState[] state)
839 throws UnknownPropertyException, WrappedTargetException
841 PropertyData p = properties.get(name);
842 if (p == null) {
843 throw new UnknownPropertyException(name, object);
845 XIdlField2 field = UnoRuntime.queryInterface(
846 XIdlField2.class, idlClass.getField(name));
847 Object value;
848 try {
849 value = field.get(
850 new Any(type, UnoRuntime.queryInterface(type, object)));
851 } catch (com.sun.star.lang.IllegalArgumentException e) {
852 throw new RuntimeException(e);
853 } catch (WrappedTargetRuntimeException e) {
854 //FIXME A WrappedTargetRuntimeException from XIdlField2.get is not
855 // guaranteed to originate directly within XIdlField2.get (and thus
856 // have the expected semantics); it might also be passed through
857 // from lower layers.
858 if (new Type(UnknownPropertyException.class).isSupertypeOf(
859 AnyConverter.getType(e.TargetException))
860 && (p.property.Attributes & PropertyAttribute.OPTIONAL) != 0)
862 throw new UnknownPropertyException(e, name, object);
863 } else {
864 throw new WrappedTargetException(e.getCause(),
865 e.getMessage(), object, e.TargetException);
868 boolean undoAmbiguous
869 = (p.property.Attributes & PropertyAttribute.MAYBEAMBIGUOUS) != 0;
870 boolean undoDefaulted
871 = (p.property.Attributes & PropertyAttribute.MAYBEDEFAULT) != 0;
872 boolean undoOptional
873 = (p.property.Attributes & PropertyAttribute.MAYBEVOID) != 0;
874 boolean isAmbiguous = false;
875 boolean isDefaulted = false;
876 while (undoAmbiguous || undoDefaulted || undoOptional) {
877 String typeName = AnyConverter.getType(value).getTypeName();
878 if (undoAmbiguous
879 && typeName.startsWith("com.sun.star.beans.Ambiguous<"))
881 XIdlClass ambiguous = getReflection(typeName);
882 try {
883 isAmbiguous = AnyConverter.toBoolean(
884 UnoRuntime.queryInterface(
885 XIdlField2.class,
886 ambiguous.getField("IsAmbiguous")).get(value));
887 value = UnoRuntime.queryInterface(
888 XIdlField2.class,
889 ambiguous.getField("Value")).get(value);
890 } catch (com.sun.star.lang.IllegalArgumentException e) {
891 throw new RuntimeException(e);
893 undoAmbiguous = false;
894 } else if (undoDefaulted
895 && typeName.startsWith("com.sun.star.beans.Defaulted<"))
897 XIdlClass defaulted = getReflection(typeName);
898 try {
899 isDefaulted = AnyConverter.toBoolean(
900 UnoRuntime.queryInterface(
901 XIdlField2.class,
902 defaulted.getField("IsDefaulted")).get(value));
903 value = UnoRuntime.queryInterface(
904 XIdlField2.class,
905 defaulted.getField("Value")).get(value);
906 } catch (com.sun.star.lang.IllegalArgumentException e) {
907 throw new RuntimeException(e);
909 undoDefaulted = false;
910 } else if (undoOptional
911 && typeName.startsWith("com.sun.star.beans.Optional<"))
913 XIdlClass optional = getReflection(typeName);
914 try {
915 boolean present = AnyConverter.toBoolean(
916 UnoRuntime.queryInterface(
917 XIdlField2.class,
918 optional.getField("IsPresent")).get(value));
919 if (!present) {
920 value = Any.VOID;
921 break;
923 value = UnoRuntime.queryInterface(
924 XIdlField2.class,
925 optional.getField("Value")).get(value);
926 } catch (com.sun.star.lang.IllegalArgumentException e) {
927 throw new RuntimeException(e);
929 undoOptional = false;
930 } else {
931 throw new RuntimeException(
932 "unexpected type of attribute " + name);
935 if (state != null) {
936 //XXX If isAmbiguous && isDefaulted, arbitrarily choose
937 // AMBIGUOUS_VALUE over DEFAULT_VALUE:
938 state[0] = isAmbiguous
939 ? PropertyState.AMBIGUOUS_VALUE
940 : isDefaulted
941 ? PropertyState.DEFAULT_VALUE : PropertyState.DIRECT_VALUE;
943 return value;
946 private Object wrapValue(
947 Object value, XIdlClass type, boolean wrapAmbiguous,
948 boolean isAmbiguous, boolean wrapDefaulted, boolean isDefaulted,
949 boolean wrapOptional)
951 if (wrapAmbiguous
952 && type.getName().startsWith("com.sun.star.beans.Ambiguous<"))
954 Object[] strct = new Object[1];
955 type.createObject(strct);
956 try {
957 XIdlField2 field = UnoRuntime.queryInterface(
958 XIdlField2.class, type.getField("Value"));
959 field.set(
960 strct,
961 wrapValue(
962 value, field.getType(), false, false, wrapDefaulted,
963 isDefaulted, wrapOptional));
964 UnoRuntime.queryInterface(
965 XIdlField2.class, type.getField("IsAmbiguous")).set(
966 strct, Boolean.valueOf(isAmbiguous));
967 } catch (com.sun.star.lang.IllegalArgumentException e) {
968 throw new RuntimeException(e);
969 } catch (com.sun.star.lang.IllegalAccessException e) {
970 throw new RuntimeException(e);
972 return strct[0];
973 } else if (wrapDefaulted
974 && type.getName().startsWith(
975 "com.sun.star.beans.Defaulted<"))
977 Object[] strct = new Object[1];
978 type.createObject(strct);
979 try {
980 XIdlField2 field = UnoRuntime.queryInterface(
981 XIdlField2.class, type.getField("Value"));
982 field.set(
983 strct,
984 wrapValue(
985 value, field.getType(), wrapAmbiguous, isAmbiguous,
986 false, false, wrapOptional));
987 UnoRuntime.queryInterface(
988 XIdlField2.class, type.getField("IsDefaulted")).set(
989 strct, Boolean.valueOf(isDefaulted));
990 } catch (com.sun.star.lang.IllegalArgumentException e) {
991 throw new RuntimeException(e);
992 } catch (com.sun.star.lang.IllegalAccessException e) {
993 throw new RuntimeException(e);
995 return strct[0];
996 } else if (wrapOptional
997 && type.getName().startsWith("com.sun.star.beans.Optional<"))
999 Object[] strct = new Object[1];
1000 type.createObject(strct);
1001 boolean present = !AnyConverter.isVoid(value);
1002 try {
1003 UnoRuntime.queryInterface(
1004 XIdlField2.class, type.getField("IsPresent")).set(
1005 strct, Boolean.valueOf(present));
1006 if (present) {
1007 XIdlField2 field = UnoRuntime.queryInterface(
1008 XIdlField2.class, type.getField("Value"));
1009 field.set(
1010 strct,
1011 wrapValue(
1012 value, field.getType(), wrapAmbiguous, isAmbiguous,
1013 wrapDefaulted, isDefaulted, false));
1015 } catch (com.sun.star.lang.IllegalArgumentException e) {
1016 throw new RuntimeException(e);
1017 } catch (com.sun.star.lang.IllegalAccessException e) {
1018 throw new RuntimeException(e);
1020 return strct[0];
1021 } else {
1022 if (wrapAmbiguous || wrapDefaulted || wrapOptional) {
1023 throw new RuntimeException("unexpected type of attribute");
1025 return value;
1029 private static XTypeDescription resolveTypedefs(XTypeDescription type) {
1030 while (type.getTypeClass() == TypeClass.TYPEDEF) {
1031 type = UnoRuntime.queryInterface(
1032 XIndirectTypeDescription.class, type).getReferencedType();
1034 return type;
1037 private PropertyData get(Object object, String propertyName)
1038 throws UnknownPropertyException
1040 PropertyData p = properties.get(propertyName);
1041 if (p == null || !p.present) {
1042 throw new UnknownPropertyException(propertyName, object);
1044 return p;
1047 private void checkUnknown(String propertyName)
1048 throws UnknownPropertyException
1050 if (propertyName.length() != 0) {
1051 get(this, propertyName);
1055 private static final class PropertyData {
1056 public PropertyData(Property property, boolean present) {
1057 this.property = property;
1058 this.present = present;
1061 public final Property property;
1062 public final boolean present;
1065 private final class Info extends WeakBase implements XPropertySetInfo
1067 public Info(Map<String,PropertyData> properties) {
1068 this.properties = properties;
1071 public Property[] getProperties() {
1072 ArrayList<Property> al = new ArrayList<Property>(properties.size());
1073 for (Iterator<PropertyData> i = properties.values().iterator(); i.hasNext();) {
1074 PropertyData p = i.next();
1075 if (p.present) {
1076 al.add(p.property);
1079 return al.toArray(new Property[al.size()]);
1082 public Property getPropertyByName(String name)
1083 throws UnknownPropertyException
1085 return get(this, name).property;
1088 public boolean hasPropertyByName(String name) {
1089 PropertyData p = properties.get(name);
1090 return p != null && p.present;
1093 private final Map<String,PropertyData> properties;
1096 private final XComponentContext context;
1097 private final XInterface object;
1098 private final Type type;
1099 private final String[] absentOptional;
1100 private final XIdlClass idlClass;
1101 private final Map<String,PropertyData> properties; // from String to Property
1102 private final String[] handleMap;
1104 private HashMap<String,ArrayList<XPropertyChangeListener>> boundListeners
1105 = new HashMap<String,ArrayList<XPropertyChangeListener>>();
1106 // from String to Vector of XPropertyChangeListener
1107 private HashMap<String,ArrayList<XVetoableChangeListener>> vetoListeners
1108 = new HashMap<String,ArrayList<XVetoableChangeListener>>();
1109 // from String to Vector of XVetoableChangeListener
1110 private boolean disposed = false;