KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > mbeanserver > OpenConverter


1 /*
2  * @(#)OpenConverter.java 1.22 07/10/25
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.jmx.mbeanserver;
9
10 import static com.sun.jmx.mbeanserver.Util.*;
11
12 import com.sun.jmx.remote.util.EnvHelp;
13
14 import java.beans.ConstructorProperties JavaDoc;
15 import java.io.InvalidObjectException JavaDoc;
16 import java.lang.ref.WeakReference JavaDoc;
17 import java.lang.reflect.Array JavaDoc;
18 import java.lang.reflect.Constructor JavaDoc;
19 import java.lang.reflect.Field JavaDoc;
20 import java.lang.reflect.GenericArrayType JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.lang.reflect.ParameterizedType JavaDoc;
24 import java.lang.reflect.Proxy JavaDoc;
25 import java.lang.reflect.Type JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.BitSet JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.Comparator JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.SortedMap JavaDoc;
36 import java.util.SortedSet JavaDoc;
37 import java.util.TreeSet JavaDoc;
38 import java.util.WeakHashMap JavaDoc;
39 import javax.management.JMX JavaDoc;
40 import javax.management.ObjectName JavaDoc;
41 import javax.management.openmbean.ArrayType JavaDoc;
42 import javax.management.openmbean.CompositeData JavaDoc;
43 import javax.management.openmbean.CompositeDataInvocationHandler JavaDoc;
44 import javax.management.openmbean.CompositeDataSupport JavaDoc;
45 import javax.management.openmbean.CompositeDataView JavaDoc;
46 import javax.management.openmbean.CompositeType JavaDoc;
47 import javax.management.openmbean.OpenDataException JavaDoc;
48 import javax.management.openmbean.OpenType JavaDoc;
49 import javax.management.openmbean.SimpleType JavaDoc;
50 import javax.management.openmbean.TabularData JavaDoc;
51 import javax.management.openmbean.TabularDataSupport JavaDoc;
52 import javax.management.openmbean.TabularType JavaDoc;
53 import static javax.management.openmbean.SimpleType JavaDoc.*;
54
55 /**
56    <p>A converter between Java types and the limited set of classes
57    defined by Open MBeans.</p>
58
59    <p>A Java type is an instance of java.lang.reflect.Type. For our
60    purposes, it is either a Class, such as String.class or int.class;
61    or a ParameterizedType, such as List<String> or Map<Integer,
62    String[]>. On J2SE 1.4 and earlier, it can only be a Class.</p>
63
64    <p>Each Type is associated with an OpenConverter. The
65    OpenConverter defines an OpenType corresponding to the Type, plus a
66    Java class corresponding to the OpenType. For example:</p>
67
68    <pre>
69    Type Open class OpenType
70    ---- ---------- --------
71    Integer Integer SimpleType.INTEGER
72    int int SimpleType.INTEGER
73    Integer[] Integer[] ArrayType(1, SimpleType.INTEGER)
74    int[] Integer[] ArrayType(SimpleType.INTEGER, true)
75    String[][] String[][] ArrayType(2, SimpleType.STRING)
76    List<String> String[] ArrayType(1, SimpleType.STRING)
77    ThreadState (an Enum) String SimpleType.STRING
78    Map<Integer, String[]> TabularData TabularType(
79                              CompositeType(
80                            {"key", SimpleType.INTEGER},
81                            {"value",
82                              ArrayType(1,
83                           SimpleType.STRING)}),
84                          indexNames={"key"})
85    </pre>
86
87    <p>Apart from simple types, arrays, and collections, Java types are
88    converted through introspection into CompositeType. The Java type
89    must have at least one getter (method such as "int getSize()" or
90    "boolean isBig()"), and we must be able to deduce how to
91    reconstruct an instance of the Java class from the values of the
92    getters using one of various heuristics.</p>
93
94    @since 1.6
95  */

96 public abstract class OpenConverter {
97     private OpenConverter(Type JavaDoc targetType, OpenType JavaDoc openType,
98               Class JavaDoc openClass) {
99     this.targetType = targetType;
100     this.openType = openType;
101     this.openClass = openClass;
102     }
103
104     /** <p>Convert an instance of openClass into an instance of targetType. */
105     public final Object JavaDoc fromOpenValue(MXBeanLookup lookup, Object JavaDoc value)
106             throws InvalidObjectException JavaDoc {
107         if (value == null)
108             return null;
109         else
110             return fromNonNullOpenValue(lookup, value);
111     }
112     
113     abstract Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
114             throws InvalidObjectException JavaDoc;
115
116     /** <p>Throw an appropriate InvalidObjectException if we will not be able
117         to convert back from the open data to the original Java object.</p> */

118     void checkReconstructible() throws InvalidObjectException JavaDoc {
119         // subclasses override if action necessary
120
}
121
122     /** <p>Convert an instance of targetType into an instance of openClass. */
123     final Object JavaDoc toOpenValue(MXBeanLookup lookup, Object JavaDoc value)
124             throws OpenDataException JavaDoc {
125         if (value == null)
126             return null;
127         else
128             return toNonNullOpenValue(lookup, value);
129     }
130     
131     abstract Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
132             throws OpenDataException JavaDoc;
133
134     /** <p>True if and only if this OpenConverter's toOpenValue and fromOpenValue
135     methods are the identity function.</p> */

136     boolean isIdentity() {
137     return false;
138     }
139
140     final Type JavaDoc getTargetType() {
141     return targetType;
142     }
143
144     final OpenType JavaDoc getOpenType() {
145     return openType;
146     }
147
148     /* The Java class corresponding to getOpenType(). This is the class
149        named by getOpenType().getClassName(), except that it may be a
150        primitive type or an array of primitive type. */

151     final Class JavaDoc getOpenClass() {
152     return openClass;
153     }
154
155     private final Type JavaDoc targetType;
156     private final OpenType JavaDoc openType;
157     private final Class JavaDoc openClass;
158     
159     private static final class ConverterMap
160         extends WeakHashMap JavaDoc<Type JavaDoc, WeakReference JavaDoc<OpenConverter>> {}
161
162     private static final ConverterMap converterMap = new ConverterMap();
163
164     /** Following List simply serves to keep a reference to predefined
165         OpenConverters so they don't get garbage collected. */

166     private static final List JavaDoc<OpenConverter> permanentConverters = newList();
167
168     private static synchronized OpenConverter getConverter(Type JavaDoc type) {
169         // Work around bug 5041784:
170
if (type instanceof GenericArrayType JavaDoc) {
171             Type JavaDoc component = ((GenericArrayType JavaDoc) type).getGenericComponentType();
172             if (component instanceof Class JavaDoc)
173                 type = Array.newInstance((Class JavaDoc<?>) component, 0).getClass();
174         }
175
176         WeakReference JavaDoc<OpenConverter> wr = converterMap.get(type);
177         return (wr == null) ? null : wr.get();
178     }
179
180     private static synchronized void putConverter(Type JavaDoc type,
181                                                   OpenConverter conv) {
182         WeakReference JavaDoc<OpenConverter> wr =
183             new WeakReference JavaDoc<OpenConverter>(conv);
184         converterMap.put(type, wr);
185     }
186     
187     private static synchronized void putPermanentConverter(Type JavaDoc type,
188                                                            OpenConverter conv) {
189         putConverter(type, conv);
190         permanentConverters.add(conv);
191     }
192
193     static {
194     /* Set up the mappings for Java types that map to SimpleType. */
195
196     final OpenType JavaDoc[] simpleTypes = {
197         BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
198         DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
199         VOID,
200     };
201
202     for (int i = 0; i < simpleTypes.length; i++) {
203         final OpenType JavaDoc t = simpleTypes[i];
204         Class JavaDoc c;
205         try {
206         c = Class.forName(t.getClassName(), false,
207                   ObjectName JavaDoc.class.getClassLoader());
208         } catch (ClassNotFoundException JavaDoc e) {
209         // the classes that these predefined types declare must exist!
210
throw new Error JavaDoc(e);
211         }
212         final OpenConverter conv = new IdentityConverter(c, t, c);
213             putPermanentConverter(c, conv);
214
215             if (c.getName().startsWith("java.lang.")) {
216         try {
217             final Field JavaDoc typeField = c.getField("TYPE");
218             final Class JavaDoc primitiveType = (Class JavaDoc) typeField.get(null);
219             final OpenConverter primitiveConv =
220             new IdentityConverter(primitiveType, t, primitiveType);
221                     putPermanentConverter(primitiveType,
222                                           primitiveConv);
223                     if (primitiveType != void.class) {
224                         final Class JavaDoc primitiveArrayType =
225                             Array.newInstance(primitiveType, 0).getClass();
226                         final OpenType JavaDoc primitiveArrayOpenType =
227                             ArrayType.getPrimitiveArrayType(primitiveArrayType);
228                         final OpenConverter primitiveArrayConv =
229                             new IdentityConverter(primitiveArrayType,
230                                                   primitiveArrayOpenType,
231                                                   primitiveArrayType);
232                         putPermanentConverter(primitiveArrayType,
233                                               primitiveArrayConv);
234                     }
235         } catch (NoSuchFieldException JavaDoc e) {
236             // OK: must not be a primitive wrapper
237
} catch (IllegalAccessException JavaDoc e) {
238             // Should not reach here
239
assert(false);
240         }
241         }
242     }
243     }
244
245     /** Get the converter for the given Java type, creating it if necessary. */
246     public static synchronized OpenConverter toConverter(Type JavaDoc objType)
247         throws OpenDataException JavaDoc {
248         
249         if (inProgress.containsKey(objType))
250             throw new OpenDataException JavaDoc("Recursive data structure");
251         
252         OpenConverter conv;
253
254     conv = getConverter(objType);
255     if (conv != null)
256             return conv;
257         
258         inProgress.put(objType, objType);
259         try {
260             conv = makeConverter(objType);
261         } finally {
262             inProgress.remove(objType);
263         }
264
265     putConverter(objType, conv);
266         return conv;
267     }
268     
269     private static OpenConverter makeConverter(Type JavaDoc objType)
270             throws OpenDataException JavaDoc {
271         
272     /* It's not yet worth formalizing these tests by having for example
273        an array of factory classes, each of which says whether it
274        recognizes the Type (Chain of Responsibility pattern). */

275         if (objType instanceof GenericArrayType JavaDoc) {
276             Type JavaDoc componentType =
277                 ((GenericArrayType JavaDoc) objType).getGenericComponentType();
278             return makeArrayOrCollectionConverter(objType, componentType);
279         } else if (objType instanceof Class JavaDoc) {
280             Class JavaDoc objClass = (Class JavaDoc<?>) objType;
281             if (objClass.isEnum()) {
282                 return makeEnumConverter(objClass);
283             } else if (objClass.isArray()) {
284                 Type JavaDoc componentType = objClass.getComponentType();
285                 return makeArrayOrCollectionConverter(objClass, componentType);
286             } else if (JMX.isMXBeanInterface(objClass)) {
287                 return makeMXBeanConverter(objClass);
288             } else {
289                 return makeCompositeConverter(objClass);
290             }
291         } else if (objType instanceof ParameterizedType JavaDoc) {
292             return makeParameterizedConverter((ParameterizedType JavaDoc) objType);
293         } else
294             throw new OpenDataException JavaDoc("Cannot map type: " + objType);
295     }
296
297     private static <T extends Enum JavaDoc<T>> OpenConverter
298         makeEnumConverter(Class JavaDoc<T> enumClass) {
299     return new EnumConverter<T>(enumClass);
300     }
301
302     /* Make the converter for an array type, or a collection such as
303      * List<String> or Set<Integer>. We never see one-dimensional
304      * primitive arrays (e.g. int[]) here because they use the identity
305      * converter and are registered as such in the static initializer.
306      */

307     private static OpenConverter
308     makeArrayOrCollectionConverter(Type JavaDoc collectionType, Type JavaDoc elementType)
309         throws OpenDataException JavaDoc {
310
311         final OpenConverter elementConverter = toConverter(elementType);
312     final OpenType JavaDoc elementOpenType = elementConverter.getOpenType();
313     final ArrayType JavaDoc openType = new ArrayType JavaDoc(1, elementOpenType);
314     final Class JavaDoc elementOpenClass = elementConverter.getOpenClass();
315
316     final Class JavaDoc openArrayClass;
317         final String JavaDoc openArrayClassName;
318         if (elementOpenClass.isArray())
319             openArrayClassName = "[" + elementOpenClass.getName();
320         else
321             openArrayClassName = "[L" + elementOpenClass.getName() + ";";
322         try {
323             openArrayClass = Class.forName(openArrayClassName);
324         } catch (ClassNotFoundException JavaDoc e) {
325             throw openDataException("Cannot obtain array class", e);
326         }
327
328     if (collectionType instanceof ParameterizedType JavaDoc) {
329         return new CollectionConverter(collectionType,
330                        openType, openArrayClass,
331                        elementConverter);
332         } else {
333             if (elementConverter.isIdentity()) {
334         return new IdentityConverter(collectionType,
335                                              openType,
336                                              openArrayClass);
337         } else {
338         return new ArrayConverter(collectionType,
339                                           openType,
340                                           openArrayClass,
341                                           elementConverter);
342         }
343     }
344     }
345
346     private static final String JavaDoc[] keyArray = {"key"};
347     private static final String JavaDoc[] keyValueArray = {"key", "value"};
348
349     private static OpenConverter
350     makeTabularConverter(Type JavaDoc objType, boolean sortedMap,
351                              Type JavaDoc keyType, Type JavaDoc valueType)
352         throws OpenDataException JavaDoc {
353
354     final String JavaDoc objTypeName = objType.toString();
355     final OpenConverter keyConverter = toConverter(keyType);
356     final OpenConverter valueConverter = toConverter(valueType);
357     final OpenType JavaDoc keyOpenType = keyConverter.getOpenType();
358     final OpenType JavaDoc valueOpenType = valueConverter.getOpenType();
359     final CompositeType JavaDoc rowType =
360         new CompositeType JavaDoc(objTypeName,
361                   objTypeName,
362                   keyValueArray,
363                   keyValueArray,
364                   new OpenType JavaDoc[] {keyOpenType, valueOpenType});
365     final TabularType JavaDoc tabularType =
366         new TabularType JavaDoc(objTypeName, objTypeName, rowType, keyArray);
367     return new TabularConverter(objType, sortedMap, tabularType,
368                     keyConverter, valueConverter);
369     }
370
371     /* We know how to translate List<E>, Set<E>, SortedSet<E>,
372        Map<K,V>, SortedMap<K,V>, and that's it. We don't accept
373        subtypes of those because we wouldn't know how to deserialize
374        them. We don't accept Queue<E> because it is unlikely people
375        would use that as a parameter or return type in an MBean. */

376     private static OpenConverter
377     makeParameterizedConverter(ParameterizedType JavaDoc objType) throws OpenDataException JavaDoc {
378
379         final Type JavaDoc rawType = objType.getRawType();
380
381         if (rawType instanceof Class JavaDoc) {
382             Class JavaDoc c = (Class JavaDoc<?>) rawType;
383             if (c == List JavaDoc.class || c == Set JavaDoc.class || c == SortedSet JavaDoc.class) {
384                 Type JavaDoc[] actuals =
385                     ((ParameterizedType JavaDoc) objType).getActualTypeArguments();
386                 assert(actuals.length == 1);
387                 if (c == SortedSet JavaDoc.class)
388                     mustBeComparable(c, actuals[0]);
389                 return makeArrayOrCollectionConverter(objType, actuals[0]);
390             } else {
391                 boolean sortedMap = (c == SortedMap JavaDoc.class);
392                 if (c == Map JavaDoc.class || sortedMap) {
393                     Type JavaDoc[] actuals =
394                             ((ParameterizedType JavaDoc) objType).getActualTypeArguments();
395                     assert(actuals.length == 2);
396                     if (sortedMap)
397                         mustBeComparable(c, actuals[0]);
398                     return makeTabularConverter(objType, sortedMap,
399                             actuals[0], actuals[1]);
400                 }
401             }
402     }
403         throw new OpenDataException JavaDoc("Cannot convert type: " + objType);
404     }
405
406     private static OpenConverter makeMXBeanConverter(Type JavaDoc t)
407             throws OpenDataException JavaDoc {
408         return new MXBeanConverter(t);
409     }
410     
411     private static OpenConverter makeCompositeConverter(Class JavaDoc c)
412         throws OpenDataException JavaDoc {
413         
414         // For historical reasons GcInfo implements CompositeData but we
415
// shouldn't count its CompositeData.getCompositeType() field as
416
// an item in the computed CompositeType.
417
final boolean gcInfoHack =
418             (c.getName().equals("com.sun.management.GcInfo") &&
419                 c.getClassLoader() == null);
420
421     final List JavaDoc<Method JavaDoc> methods =
422                 MBeanAnalyzer.eliminateCovariantMethods(c.getMethods());
423     final SortedMap JavaDoc<String JavaDoc,Method JavaDoc> getterMap = newSortedMap();
424
425     /* Select public methods that look like "T getX()" or "boolean
426        isX()", where T is not void and X is not the empty
427        string. Exclude "Class getClass()" inherited from Object. */

428     for (Method JavaDoc method : methods) {
429         final String JavaDoc propertyName = propertyName(method);
430
431         if (propertyName == null)
432         continue;
433             if (gcInfoHack && propertyName.equals("CompositeType"))
434                 continue;
435
436             Method JavaDoc old =
437                 getterMap.put(decapitalize(propertyName),
438                             method);
439             if (old != null) {
440                 final String JavaDoc msg =
441                     "Class " + c.getName() + " has method name clash: " +
442                     old.getName() + ", " + method.getName();
443                 throw new OpenDataException JavaDoc(msg);
444             }
445     }
446
447         final int nitems = getterMap.size();
448
449     if (nitems == 0) {
450         throw new OpenDataException JavaDoc("Can't map " + c.getName() +
451                     " to an open data type");
452     }
453
454         final Method JavaDoc[] getters = new Method JavaDoc[nitems];
455         final String JavaDoc[] itemNames = new String JavaDoc[nitems];
456         final OpenType JavaDoc[] openTypes = new OpenType JavaDoc[nitems];
457         int i = 0;
458         for (Map.Entry JavaDoc<String JavaDoc,Method JavaDoc> entry : getterMap.entrySet()) {
459             itemNames[i] = entry.getKey();
460             final Method JavaDoc getter = entry.getValue();
461             getters[i] = getter;
462             final Type JavaDoc retType = getter.getGenericReturnType();
463             openTypes[i] = toConverter(retType).getOpenType();
464             i++;
465         }
466
467         CompositeType JavaDoc compositeType =
468         new CompositeType JavaDoc(c.getName(),
469                   c.getName(),
470                   itemNames, // field names
471
itemNames, // field descriptions
472
openTypes);
473
474     return new CompositeConverter(c,
475                       compositeType,
476                       itemNames,
477                       getters);
478     }
479
480     /* Converter for classes where the open data is identical to the
481        original data. This is true for any of the SimpleType types,
482        and for an any-dimension array of those. It is also true for
483        primitive types as of JMX 1.3, since an int[] needs to
484        can be directly represented by an ArrayType, and an int needs no mapping
485        because reflection takes care of it. */

486     private static final class IdentityConverter extends OpenConverter {
487     IdentityConverter(Type JavaDoc targetType, OpenType JavaDoc openType,
488                           Class JavaDoc openClass) {
489         super(targetType, openType, openClass);
490     }
491
492     boolean isIdentity() {
493         return true;
494     }
495
496     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value) {
497         return value;
498     }
499
500     public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value) {
501         return value;
502     }
503     }
504
505     private static final class EnumConverter<T extends Enum JavaDoc<T>>
506         extends OpenConverter {
507
508     EnumConverter(Class JavaDoc<T> enumClass) {
509         super(enumClass, SimpleType.STRING, String JavaDoc.class);
510         this.enumClass = enumClass;
511     }
512
513     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value) {
514         return ((Enum JavaDoc) value).name();
515     }
516
517         // return type could be T, but after erasure that would be
518
// java.lang.Enum, which doesn't exist on J2SE 1.4
519
public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
520                 throws InvalidObjectException JavaDoc {
521         try {
522         return Enum.valueOf(enumClass, (String JavaDoc) value);
523         } catch (Exception JavaDoc e) {
524         throw invalidObjectException("Cannot convert to enum: " +
525                                              value, e);
526         }
527     }
528
529     private final Class JavaDoc<T> enumClass;
530     }
531
532     private static final class ArrayConverter extends OpenConverter {
533     ArrayConverter(Type JavaDoc targetType,
534                ArrayType JavaDoc openArrayType, Class JavaDoc openArrayClass,
535                OpenConverter elementConverter) {
536         super(targetType, openArrayType, openArrayClass);
537         this.elementConverter = elementConverter;
538     }
539
540     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
541                 throws OpenDataException JavaDoc {
542         Object JavaDoc[] valueArray = (Object JavaDoc[]) value;
543         final int len = valueArray.length;
544             final Object JavaDoc[] openArray = (Object JavaDoc[])
545         Array.newInstance(getOpenClass().getComponentType(), len);
546         for (int i = 0; i < len; i++) {
547         openArray[i] =
548                     elementConverter.toOpenValue(lookup, valueArray[i]);
549             }
550         return openArray;
551     }
552
553     public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc openValue)
554                 throws InvalidObjectException JavaDoc {
555         final Object JavaDoc[] openArray = (Object JavaDoc[]) openValue;
556         final Type JavaDoc targetType = getTargetType();
557         final Object JavaDoc[] valueArray;
558             final Type JavaDoc componentType;
559             if (targetType instanceof GenericArrayType JavaDoc) {
560                 componentType =
561                     ((GenericArrayType JavaDoc) targetType).getGenericComponentType();
562             } else if (targetType instanceof Class JavaDoc &&
563                        ((Class JavaDoc<?>) targetType).isArray()) {
564                 componentType = ((Class JavaDoc<?>) targetType).getComponentType();
565             } else {
566                 throw new IllegalArgumentException JavaDoc("Not an array: " +
567                                                    targetType);
568             }
569             valueArray = (Object JavaDoc[]) Array.newInstance((Class JavaDoc<?>) componentType,
570                                                       openArray.length);
571         for (int i = 0; i < openArray.length; i++) {
572         valueArray[i] =
573             elementConverter.fromOpenValue(lookup, openArray[i]);
574         }
575         return valueArray;
576     }
577
578         void checkReconstructible() throws InvalidObjectException JavaDoc {
579             elementConverter.checkReconstructible();
580         }
581
582     /** OpenConverter for the elements of this array. If this is an
583         array of arrays, the converter converts the second-level arrays,
584         not the deepest elements. */

585     private final OpenConverter elementConverter;
586     }
587
588     private static final class CollectionConverter extends OpenConverter {
589     CollectionConverter(Type JavaDoc targetType,
590                 ArrayType JavaDoc openArrayType,
591                 Class JavaDoc openArrayClass,
592                 OpenConverter elementConverter) {
593         super(targetType, openArrayType, openArrayClass);
594         this.elementConverter = elementConverter;
595
596         /* Determine the concrete class to be used when converting
597            back to this Java type. We convert all Lists to ArrayList
598            and all Sets to TreeSet. (TreeSet because it is a SortedSet,
599            so works for both Set and SortedSet.) */

600             Type JavaDoc raw = ((ParameterizedType JavaDoc) targetType).getRawType();
601             Class JavaDoc c = (Class JavaDoc<?>) raw;
602         if (c == List JavaDoc.class)
603         collectionClass = ArrayList JavaDoc.class;
604         else if (c == Set JavaDoc.class)
605                 collectionClass = HashSet JavaDoc.class;
606             else if (c == SortedSet JavaDoc.class)
607         collectionClass = TreeSet JavaDoc.class;
608         else { // can't happen
609
assert(false);
610         collectionClass = null;
611         }
612     }
613
614     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
615                 throws OpenDataException JavaDoc {
616         final Collection JavaDoc valueCollection = (Collection JavaDoc) value;
617             if (valueCollection instanceof SortedSet JavaDoc) {
618                 Comparator JavaDoc comparator =
619                     ((SortedSet JavaDoc) valueCollection).comparator();
620                 if (comparator != null) {
621                     final String JavaDoc msg =
622                         "Cannot convert SortedSet with non-null comparator: " +
623                         comparator;
624                     IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
625                     OpenDataException JavaDoc ode = new OpenDataException JavaDoc(msg);
626                     ode.initCause(iae);
627                     throw ode;
628                 }
629             }
630         final Object JavaDoc[] openArray = (Object JavaDoc[])
631         Array.newInstance(getOpenClass().getComponentType(),
632                   valueCollection.size());
633         int i = 0;
634         for (Object JavaDoc o : valueCollection)
635         openArray[i++] = elementConverter.toOpenValue(lookup, o);
636         return openArray;
637     }
638
639     public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc openValue)
640                 throws InvalidObjectException JavaDoc {
641         final Object JavaDoc[] openArray = (Object JavaDoc[]) openValue;
642         final Collection JavaDoc<Object JavaDoc> valueCollection;
643         try {
644         valueCollection = collectionClass.newInstance();
645         } catch (Exception JavaDoc e) {
646                 throw invalidObjectException("Cannot create collection", e);
647         }
648         for (Object JavaDoc o : openArray) {
649                 Object JavaDoc value = elementConverter.fromOpenValue(lookup, o);
650         if (!valueCollection.add(value)) {
651             final String JavaDoc msg =
652             "Could not add " + o + " to " +
653             collectionClass.getName() +
654             " (duplicate set element?)";
655             throw new InvalidObjectException JavaDoc(msg);
656         }
657         }
658         return valueCollection;
659     }
660
661         void checkReconstructible() throws InvalidObjectException JavaDoc {
662             elementConverter.checkReconstructible();
663         }
664
665     private final Class JavaDoc<? extends Collection JavaDoc> collectionClass;
666     private final OpenConverter elementConverter;
667     }
668
669     private static final class MXBeanConverter extends OpenConverter {
670         MXBeanConverter(Type JavaDoc intf) {
671             super(intf, SimpleType.OBJECTNAME, ObjectName JavaDoc.class);
672         }
673
674         final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
675                 throws OpenDataException JavaDoc {
676             lookupNotNull(lookup, OpenDataException JavaDoc.class);
677             ObjectName JavaDoc name = lookup.mxbeanToObjectName(value);
678             if (name == null)
679                 throw new OpenDataException JavaDoc("No name for object: " + value);
680             return name;
681         }
682
683         public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
684                 throws InvalidObjectException JavaDoc {
685             lookupNotNull(lookup, InvalidObjectException JavaDoc.class);
686             ObjectName JavaDoc name = (ObjectName JavaDoc) value;
687             Object JavaDoc mxbean =
688                 lookup.objectNameToMXBean(name, (Class JavaDoc<?>) getTargetType());
689             if (mxbean == null) {
690                 final String JavaDoc msg =
691                     "No MXBean for name: " + name;
692                 throw new InvalidObjectException JavaDoc(msg);
693             }
694             return mxbean;
695         }
696         
697         private <T extends Exception JavaDoc> void
698             lookupNotNull(MXBeanLookup lookup, Class JavaDoc<T> excClass)
699                 throws T {
700             if (lookup == null) {
701                 final String JavaDoc msg =
702                     "Cannot convert MXBean interface in this context";
703                 T exc;
704                 try {
705                     Constructor JavaDoc<T> con = excClass.getConstructor(String JavaDoc.class);
706                     exc = con.newInstance(msg);
707                 } catch (Exception JavaDoc e) {
708                     throw new RuntimeException JavaDoc(e);
709                 }
710                 throw exc;
711             }
712         }
713     }
714     
715     private static final class TabularConverter extends OpenConverter {
716     TabularConverter(Type JavaDoc targetType,
717                          boolean sortedMap,
718              TabularType JavaDoc tabularType,
719              OpenConverter keyConverter,
720              OpenConverter valueConverter) {
721         super(targetType, tabularType, TabularData JavaDoc.class);
722             this.sortedMap = sortedMap;
723         this.keyConverter = keyConverter;
724         this.valueConverter = valueConverter;
725     }
726
727     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
728                 throws OpenDataException JavaDoc {
729         final Map JavaDoc<Object JavaDoc, Object JavaDoc> valueMap = (Map JavaDoc<Object JavaDoc, Object JavaDoc>) value;
730             if (valueMap instanceof SortedMap JavaDoc) {
731                 Comparator JavaDoc comparator = ((SortedMap JavaDoc) valueMap).comparator();
732                 if (comparator != null) {
733                     final String JavaDoc msg =
734                         "Cannot convert SortedMap with non-null comparator: " +
735                         comparator;
736                     IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc(msg);
737                     OpenDataException JavaDoc ode = new OpenDataException JavaDoc(msg);
738                     ode.initCause(iae);
739                     throw ode;
740                 }
741             }
742         final TabularType JavaDoc tabularType = (TabularType JavaDoc) getOpenType();
743         final TabularData JavaDoc table = new TabularDataSupport JavaDoc(tabularType);
744         final CompositeType JavaDoc rowType = tabularType.getRowType();
745         for (Map.Entry JavaDoc entry : valueMap.entrySet()) {
746         final Object JavaDoc openKey =
747             keyConverter.toOpenValue(lookup, entry.getKey());
748         final Object JavaDoc openValue =
749             valueConverter.toOpenValue(lookup, entry.getValue());
750         final CompositeData JavaDoc row;
751                 row =
752                     new CompositeDataSupport JavaDoc(rowType, keyValueArray,
753                                              new Object JavaDoc[] {openKey,
754                                                            openValue});
755         table.put(row);
756         }
757         return table;
758     }
759
760     public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc openValue)
761                 throws InvalidObjectException JavaDoc {
762         final TabularData JavaDoc table = (TabularData JavaDoc) openValue;
763         final Collection JavaDoc<CompositeData JavaDoc> rows =
764         (Collection JavaDoc<CompositeData JavaDoc>) table.values();
765             final Map JavaDoc<Object JavaDoc, Object JavaDoc> valueMap =
766                 sortedMap ? newSortedMap() : newMap();
767         for (CompositeData JavaDoc row : rows) {
768         final Object JavaDoc key =
769             keyConverter.fromOpenValue(lookup, row.get("key"));
770         final Object JavaDoc value =
771             valueConverter.fromOpenValue(lookup, row.get("value"));
772         if (valueMap.put(key, value) != null) {
773             final String JavaDoc msg =
774             "Duplicate entry in TabularData: key=" + key;
775             throw new InvalidObjectException JavaDoc(msg);
776         }
777         }
778         return valueMap;
779     }
780
781         void checkReconstructible() throws InvalidObjectException JavaDoc {
782             keyConverter.checkReconstructible();
783             valueConverter.checkReconstructible();
784         }
785
786     private final boolean sortedMap;
787         private final OpenConverter keyConverter;
788     private final OpenConverter valueConverter;
789     }
790
791     private static final class CompositeConverter extends OpenConverter {
792     CompositeConverter(Class JavaDoc targetClass,
793                CompositeType JavaDoc compositeType,
794                String JavaDoc[] itemNames,
795                Method JavaDoc[] getters) throws OpenDataException JavaDoc {
796         super(targetClass, compositeType, CompositeData JavaDoc.class);
797
798         assert(itemNames.length == getters.length);
799
800         this.itemNames = itemNames;
801         this.getters = getters;
802         this.getterConverters = new OpenConverter[getters.length];
803         for (int i = 0; i < getters.length; i++) {
804         Type JavaDoc retType = getters[i].getGenericReturnType();
805         getterConverters[i] = OpenConverter.toConverter(retType);
806         }
807     }
808
809     final Object JavaDoc toNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
810                 throws OpenDataException JavaDoc {
811             CompositeType JavaDoc ct = (CompositeType JavaDoc) getOpenType();
812         if (value instanceof CompositeDataView JavaDoc)
813         return ((CompositeDataView JavaDoc) value).toCompositeData(ct);
814             if (value == null)
815                 return null;
816
817         Object JavaDoc[] values = new Object JavaDoc[getters.length];
818         for (int i = 0; i < getters.length; i++) {
819         try {
820             Object JavaDoc got = getters[i].invoke(value, (Object JavaDoc[]) null);
821             values[i] = getterConverters[i].toOpenValue(lookup, got);
822         } catch (Exception JavaDoc e) {
823             throw openDataException("Error calling getter for " +
824                                             itemNames[i] + ": " + e, e);
825         }
826         }
827         return new CompositeDataSupport JavaDoc(ct, itemNames, values);
828     }
829
830     /** Determine how to convert back from the CompositeData into
831         the original Java type. For a type that is not reconstructible,
832             this method will fail every time, and will throw the right
833             exception. */

834     private synchronized void makeCompositeBuilder()
835         throws InvalidObjectException JavaDoc {
836         if (compositeBuilder != null)
837         return;
838
839         Class JavaDoc targetClass = (Class JavaDoc<?>) getTargetType();
840             /* In this 2D array, each subarray is a set of builders where
841                there is no point in consulting the ones after the first if
842                the first refuses. */

843         CompositeBuilder[][] builders = {
844                 {
845                     new CompositeBuilderViaFrom(targetClass, itemNames),
846                 },
847                 {
848                     new CompositeBuilderViaConstructor(targetClass, itemNames),
849                 },
850                 {
851                     new CompositeBuilderCheckGetters(targetClass, itemNames,
852                                                      getterConverters),
853                     new CompositeBuilderViaSetters(targetClass, itemNames),
854                     new CompositeBuilderViaProxy(targetClass, itemNames),
855                 },
856         };
857         CompositeBuilder foundBuilder = null;
858         /* We try to make a meaningful exception message by
859            concatenating each Builder's explanation of why it
860            isn't applicable. */

861         StringBuffer JavaDoc whyNots = new StringBuffer JavaDoc();
862         find:
863         for (CompositeBuilder[] relatedBuilders : builders) {
864                 for (int i = 0; i < relatedBuilders.length; i++) {
865                     CompositeBuilder builder = relatedBuilders[i];
866                     String JavaDoc whyNot = builder.applicable(getters);
867                     if (whyNot == null) {
868                         foundBuilder = builder;
869                         break find;
870                     }
871                     if (whyNot.length() > 0) {
872                         if (whyNots.length() > 0)
873                             whyNots.append("; ");
874                         whyNots.append(whyNot);
875                         if (i == 0)
876                            break; // skip other builders in this group
877
}
878                 }
879         }
880         if (foundBuilder == null) {
881         final String JavaDoc msg =
882             "Do not know how to make a " + targetClass.getName() +
883             " from a CompositeData: " + whyNots;
884         throw new InvalidObjectException JavaDoc(msg);
885         }
886         compositeBuilder = foundBuilder;
887     }
888
889     void checkReconstructible() throws InvalidObjectException JavaDoc {
890             makeCompositeBuilder();
891         }
892
893         public final Object JavaDoc fromNonNullOpenValue(MXBeanLookup lookup, Object JavaDoc value)
894                 throws InvalidObjectException JavaDoc {
895         makeCompositeBuilder();
896         return compositeBuilder.fromCompositeData(lookup,
897                                                       (CompositeData JavaDoc) value,
898                                                       itemNames,
899                                                       getterConverters);
900     }
901
902     private final String JavaDoc[] itemNames;
903     private final Method JavaDoc[] getters;
904     private final OpenConverter[] getterConverters;
905     private CompositeBuilder compositeBuilder;
906     }
907
908     /** Converts from a CompositeData to an instance of the targetClass. */
909     private static abstract class CompositeBuilder {
910     CompositeBuilder(Class JavaDoc targetClass, String JavaDoc[] itemNames) {
911         this.targetClass = targetClass;
912             this.itemNames = itemNames;
913     }
914
915     Class JavaDoc getTargetClass() {
916         return targetClass;
917     }
918
919         String JavaDoc[] getItemNames() {
920             return itemNames;
921         }
922
923     /** If the subclass is appropriate for targetClass, then the
924         method returns null. If the subclass is not appropriate,
925         then the method returns an explanation of why not. If the
926         subclass should be appropriate but there is a problem,
927         then the method throws InvalidObjectException. */

928     abstract String JavaDoc applicable(Method JavaDoc[] getters)
929                 throws InvalidObjectException JavaDoc;
930
931         abstract Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
932                                           String JavaDoc[] itemNames,
933                                           OpenConverter[] converters)
934                 throws InvalidObjectException JavaDoc;
935
936     private final Class JavaDoc targetClass;
937         private final String JavaDoc[] itemNames;
938     }
939
940     /** Builder for when the target class has a method "public static
941     from(CompositeData)". */

942     private static final class CompositeBuilderViaFrom
943         extends CompositeBuilder {
944
945     CompositeBuilderViaFrom(Class JavaDoc targetClass, String JavaDoc[] itemNames) {
946         super(targetClass, itemNames);
947     }
948
949     String JavaDoc applicable(Method JavaDoc[] getters) throws InvalidObjectException JavaDoc {
950         // See if it has a method "T from(CompositeData)"
951
// as is conventional for a CompositeDataView
952
Class JavaDoc targetClass = getTargetClass();
953         try {
954         Method JavaDoc fromMethod =
955             targetClass.getMethod("from",
956                       new Class JavaDoc[] {CompositeData JavaDoc.class});
957
958         if (!Modifier.isStatic(fromMethod.getModifiers())) {
959             final String JavaDoc msg =
960             "Method from(CompositeData) is not static";
961             throw new InvalidObjectException JavaDoc(msg);
962         }
963
964         if (fromMethod.getReturnType() != getTargetClass()) {
965             final String JavaDoc msg =
966             "Method from(CompositeData) returns " +
967             fromMethod.getReturnType().getName() +
968             " not " + targetClass.getName();
969             throw new InvalidObjectException JavaDoc(msg);
970         }
971
972         this.fromMethod = fromMethod;
973         return null; // success!
974
} catch (InvalidObjectException JavaDoc e) {
975         throw e;
976         } catch (Exception JavaDoc e) {
977         // OK: it doesn't have the method
978
return "no method from(CompositeData)";
979         }
980     }
981
982     final Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
983                                  String JavaDoc[] itemNames,
984                                  OpenConverter[] converters)
985                 throws InvalidObjectException JavaDoc {
986         try {
987         return fromMethod.invoke(null, cd);
988         } catch (Exception JavaDoc e) {
989         final String JavaDoc msg = "Failed to invoke from(CompositeData)";
990                 throw invalidObjectException(msg, e);
991         }
992     }
993
994     private Method JavaDoc fromMethod;
995     }
996
997     /** This builder never actually returns success. It simply serves
998         to check whether the other builders in the same group have any
999         chance of success. If any getter in the targetClass returns
1000        a type that we don't know how to reconstruct, then we will
1001        not be able to make a builder, and there is no point in repeating
1002        the error about the problematic getter as many times as there are
1003        candidate builders. Instead, the "applicable" method will return
1004        an explanatory string, and the other builders will be skipped.
1005        If all the getters are OK, then the "applicable" method will return
1006        an empty string and the other builders will be tried. */

1007    private static class CompositeBuilderCheckGetters extends CompositeBuilder {
1008        CompositeBuilderCheckGetters(Class JavaDoc targetClass, String JavaDoc[] itemNames,
1009                                     OpenConverter[] getterConverters) {
1010            super(targetClass, itemNames);
1011            this.getterConverters = getterConverters;
1012        }
1013
1014        String JavaDoc applicable(Method JavaDoc[] getters) {
1015            for (int i = 0; i < getters.length; i++) {
1016                try {
1017                    getterConverters[i].checkReconstructible();
1018                } catch (InvalidObjectException JavaDoc e) {
1019                    return "method " + getters[i].getName() + " returns type " +
1020                        "that cannot be mapped back from OpenData";
1021                }
1022            }
1023            return "";
1024        }
1025        
1026        final Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
1027                                       String JavaDoc[] itemNames,
1028                                       OpenConverter[] converters) {
1029            throw new Error JavaDoc();
1030        }
1031        
1032        private final OpenConverter[] getterConverters;
1033    }
1034
1035    /** Builder for when the target class has a setter for every getter. */
1036    private static class CompositeBuilderViaSetters extends CompositeBuilder {
1037
1038        CompositeBuilderViaSetters(Class JavaDoc targetClass, String JavaDoc[] itemNames) {
1039            super(targetClass, itemNames);
1040        }
1041
1042        String JavaDoc applicable(Method JavaDoc[] getters) {
1043            try {
1044                Constructor JavaDoc c = getTargetClass().getConstructor((Class JavaDoc[]) null);
1045            } catch (Exception JavaDoc e) {
1046                return "does not have a public no-arg constructor";
1047            }
1048
1049            Method JavaDoc[] setters = new Method JavaDoc[getters.length];
1050            for (int i = 0; i < getters.length; i++) {
1051                Method JavaDoc getter = getters[i];
1052                Class JavaDoc returnType = getter.getReturnType();
1053                String JavaDoc name = propertyName(getter);
1054                String JavaDoc setterName = "set" + name;
1055                Method JavaDoc setter;
1056                try {
1057                    setter = getTargetClass().getMethod(setterName, returnType);
1058                    if (setter.getReturnType() != void.class)
1059                        throw new Exception JavaDoc();
1060                } catch (Exception JavaDoc e) {
1061                    return "not all getters have corresponding setters " +
1062                           "(" + getter + ")";
1063                }
1064                setters[i] = setter;
1065            }
1066            this.setters = setters;
1067            return null;
1068        }
1069
1070        Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
1071                                 String JavaDoc[] itemNames,
1072                                 OpenConverter[] converters)
1073                throws InvalidObjectException JavaDoc {
1074            Object JavaDoc o;
1075            try {
1076                o = getTargetClass().newInstance();
1077                for (int i = 0; i < itemNames.length; i++) {
1078            if (cd.containsKey(itemNames[i])) {
1079            Object JavaDoc openItem = cd.get(itemNames[i]);
1080            Object JavaDoc javaItem =
1081                converters[i].fromOpenValue(lookup, openItem);
1082            setters[i].invoke(o, javaItem);
1083            }
1084                }
1085            } catch (Exception JavaDoc e) {
1086                throw invalidObjectException(e);
1087            }
1088            return o;
1089        }
1090
1091        private Method JavaDoc[] setters;
1092    }
1093
1094    /** Builder for when the target class has a constructor that is
1095    annotated with @ConstructorProperties so we can see the correspondence
1096    to getters. */

1097    private static final class CompositeBuilderViaConstructor
1098        extends CompositeBuilder {
1099
1100    CompositeBuilderViaConstructor(Class JavaDoc targetClass, String JavaDoc[] itemNames) {
1101        super(targetClass, itemNames);
1102    }
1103
1104    String JavaDoc applicable(Method JavaDoc[] getters) throws InvalidObjectException JavaDoc {
1105            
1106            final Class JavaDoc<ConstructorProperties JavaDoc> propertyNamesClass = ConstructorProperties JavaDoc.class;
1107            
1108        Class JavaDoc targetClass = getTargetClass();
1109        Constructor JavaDoc[] constrs = targetClass.getConstructors();
1110            
1111            // Applicable if and only if there are any annotated constructors
1112
List JavaDoc<Constructor JavaDoc> annotatedConstrList = newList();
1113            for (Constructor JavaDoc constr : constrs) {
1114                if (Modifier.isPublic(constr.getModifiers())
1115                        && constr.getAnnotation(propertyNamesClass) != null)
1116                    annotatedConstrList.add(constr);
1117            }
1118
1119        if (annotatedConstrList.isEmpty())
1120        return "no constructor has @ConstructorProperties annotation";
1121            
1122            annotatedConstructors = newList();
1123
1124            // Now check that all the annotated constructors are valid
1125
// and throw an exception if not.
1126

1127            // First link the itemNames to their getter indexes.
1128
Map JavaDoc<String JavaDoc, Integer JavaDoc> getterMap = newMap();
1129            String JavaDoc[] itemNames = getItemNames();
1130            for (int i = 0; i < itemNames.length; i++)
1131                getterMap.put(itemNames[i], i);
1132            
1133            // Run through the constructors making the checks in the spec.
1134
// For each constructor, remember the correspondence between its
1135
// parameters and the items. The int[] for a constructor says
1136
// what parameter index should get what item. For example,
1137
// if element 0 is 2 then that means that item 0 in the
1138
// CompositeData goes to parameter 2 of the constructor. If an
1139
// element is -1, that item isn't given to the constructor.
1140
// Also remember the set of properties in that constructor
1141
// so we can test unambiguity.
1142
Set JavaDoc<BitSet JavaDoc> getterIndexSets = newSet();
1143            for (Constructor JavaDoc constr : annotatedConstrList) {
1144                String JavaDoc[] propertyNames =
1145                    constr.getAnnotation(propertyNamesClass).value();
1146                
1147                Set JavaDoc<String JavaDoc> propertyNameSet =
1148                    newSet(Arrays.asList(propertyNames));
1149
1150                Type JavaDoc[] paramTypes = constr.getGenericParameterTypes();
1151                if (paramTypes.length != propertyNames.length) {
1152                    final String JavaDoc msg =
1153                        "Number of constructor params does not match " +
1154                        "@ConstructorProperties annotation: " + constr;
1155                    throw new InvalidObjectException JavaDoc(msg);
1156                }
1157                
1158                int[] paramIndexes = new int[getters.length];
1159                for (int i = 0; i < getters.length; i++)
1160                    paramIndexes[i] = -1;
1161                BitSet JavaDoc present = new BitSet JavaDoc();
1162
1163                for (int i = 0; i < propertyNames.length; i++) {
1164                    String JavaDoc propertyName = propertyNames[i];
1165                    if (!getterMap.containsKey(propertyName)) {
1166                        final String JavaDoc msg =
1167                            "@ConstructorProperties includes name " + propertyName +
1168                            " which does not correspond to a property: " +
1169                            constr;
1170                        throw new InvalidObjectException JavaDoc(msg);
1171                    }
1172                    int getterIndex = getterMap.get(propertyName);
1173                    paramIndexes[getterIndex] = i;
1174                    if (present.get(getterIndex)) {
1175                        final String JavaDoc msg =
1176                            "@ConstructorProperties contains property " +
1177                            propertyName + " more than once: " + constr;
1178                        throw new InvalidObjectException JavaDoc(msg);
1179                    }
1180                    present.set(getterIndex);
1181                    Method JavaDoc getter = getters[getterIndex];
1182                    Type JavaDoc propertyType = getter.getGenericReturnType();
1183                    if (!propertyType.equals(paramTypes[i])) {
1184                        final String JavaDoc msg =
1185                            "@ConstructorProperties gives property " + propertyName +
1186                            " of type " + propertyType + " for parameter " +
1187                            " of type " + paramTypes[i] + ": " + constr;
1188                        throw new InvalidObjectException JavaDoc(msg);
1189                    }
1190                }
1191
1192                if (!getterIndexSets.add(present)) {
1193                    final String JavaDoc msg =
1194                        "More than one constructor has a @ConstructorProperties " +
1195                        "annotation with this set of names: " +
1196                        Arrays.toString(propertyNames);
1197                    throw new InvalidObjectException JavaDoc(msg);
1198                }
1199                
1200                Constr c = new Constr(constr, paramIndexes, present);
1201                annotatedConstructors.add(c);
1202            }
1203
1204            /* Check that no possible set of items could lead to an ambiguous
1205             * choice of constructor (spec requires this check). For any
1206             * pair of constructors, their union would be the minimal
1207             * ambiguous set. If this set itself corresponds to a constructor,
1208             * there is no ambiguity for that pair. In the usual case, one
1209             * of the constructors is a superset of the other so the union is
1210             * just the bigger constuctor.
1211         *
1212         * The algorithm here is quadratic in the number of constructors
1213         * with a @ConstructorProperties annotation. Typically this corresponds
1214         * to the number of versions of the class there have been. Ten
1215         * would already be a large number, so although it's probably
1216         * possible to have an O(n lg n) algorithm it wouldn't be
1217         * worth the complexity.
1218             */

1219            for (BitSet JavaDoc a : getterIndexSets) {
1220                boolean seen = false;
1221                for (BitSet JavaDoc b : getterIndexSets) {
1222                    if (a == b)
1223                        seen = true;
1224                    else if (seen) {
1225                        BitSet JavaDoc u = new BitSet JavaDoc();
1226                        u.or(a); u.or(b);
1227                        if (!getterIndexSets.contains(u)) {
1228                            Set JavaDoc<String JavaDoc> names = new TreeSet JavaDoc<String JavaDoc>();
1229                            for (int i = u.nextSetBit(0); i >= 0;
1230                                 i = u.nextSetBit(i+1))
1231                                names.add(itemNames[i]);
1232                            final String JavaDoc msg =
1233                                "Constructors with @ConstructorProperties annotation " +
1234                                " would be ambiguous for these items: " +
1235                                names;
1236                            throw new InvalidObjectException JavaDoc(msg);
1237                        }
1238                    }
1239                }
1240            }
1241
1242        return null; // success!
1243
}
1244
1245        Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
1246                                 String JavaDoc[] itemNames,
1247                                 OpenConverter[] converters)
1248                throws InvalidObjectException JavaDoc {
1249            // The CompositeData might come from an earlier version where
1250
// not all the items were present. We look for a constructor
1251
// that accepts just the items that are present. Because of
1252
// the ambiguity check in applicable(), we know there must be
1253
// at most one maximally applicable constructor.
1254
CompositeType JavaDoc ct = cd.getCompositeType();
1255            BitSet JavaDoc present = new BitSet JavaDoc();
1256            for (int i = 0; i < itemNames.length; i++) {
1257                if (ct.getType(itemNames[i]) != null)
1258                    present.set(i);
1259            }
1260            
1261            Constr max = null;
1262            for (Constr constr : annotatedConstructors) {
1263                if (subset(constr.presentParams, present) &&
1264                        (max == null ||
1265                         subset(max.presentParams, constr.presentParams)))
1266                    max = constr;
1267            }
1268            
1269            if (max == null) {
1270                final String JavaDoc msg =
1271                    "No constructor has a @ConstructorProperties for this set of " +
1272                    "items: " + ct.keySet();
1273                throw new InvalidObjectException JavaDoc(msg);
1274            }
1275            
1276            Object JavaDoc[] params = new Object JavaDoc[max.presentParams.cardinality()];
1277            for (int i = 0; i < itemNames.length; i++) {
1278                if (!max.presentParams.get(i))
1279                    continue;
1280                Object JavaDoc openItem = cd.get(itemNames[i]);
1281                Object JavaDoc javaItem = converters[i].fromOpenValue(lookup, openItem);
1282                int index = max.paramIndexes[i];
1283                if (index >= 0)
1284                    params[index] = javaItem;
1285            }
1286
1287        try {
1288        return max.constructor.newInstance(params);
1289        } catch (Exception JavaDoc e) {
1290        final String JavaDoc msg =
1291            "Exception constructing " + getTargetClass().getName();
1292                throw invalidObjectException(msg, e);
1293        }
1294        }
1295        
1296        private static boolean subset(BitSet JavaDoc sub, BitSet JavaDoc sup) {
1297            BitSet JavaDoc subcopy = (BitSet JavaDoc) sub.clone();
1298            subcopy.andNot(sup);
1299            return subcopy.isEmpty();
1300        }
1301        
1302        private static class Constr {
1303            final Constructor JavaDoc constructor;
1304            final int[] paramIndexes;
1305            final BitSet JavaDoc presentParams;
1306            Constr(Constructor JavaDoc constructor, int[] paramIndexes,
1307                   BitSet JavaDoc presentParams) {
1308                this.constructor = constructor;
1309                this.paramIndexes = paramIndexes;
1310                this.presentParams = presentParams;
1311            }
1312        }
1313        
1314        private List JavaDoc<Constr> annotatedConstructors;
1315    }
1316
1317    /** Builder for when the target class is an interface and contains
1318    no methods other than getters. Then we can make an instance
1319    using a dynamic proxy that forwards the getters to the source
1320    CompositeData. */

1321    private static final class CompositeBuilderViaProxy
1322        extends CompositeBuilder {
1323
1324    CompositeBuilderViaProxy(Class JavaDoc targetClass, String JavaDoc[] itemNames) {
1325        super(targetClass, itemNames);
1326    }
1327
1328    String JavaDoc applicable(Method JavaDoc[] getters) {
1329        Class JavaDoc targetClass = getTargetClass();
1330        if (!targetClass.isInterface())
1331        return "not an interface";
1332            Set JavaDoc<Method JavaDoc> methods =
1333                newSet(Arrays.asList(targetClass.getMethods()));
1334            methods.removeAll(Arrays.asList(getters));
1335            /* If the interface has any methods left over, they better be
1336             * public methods that are already present in java.lang.Object.
1337             */

1338            String JavaDoc bad = null;
1339            for (Method JavaDoc m : methods) {
1340                String JavaDoc mname = m.getName();
1341                Class JavaDoc[] mparams = m.getParameterTypes();
1342                try {
1343                    Method JavaDoc om = Object JavaDoc.class.getMethod(mname, mparams);
1344                    if (!Modifier.isPublic(om.getModifiers()))
1345                        bad = mname;
1346                } catch (NoSuchMethodException JavaDoc e) {
1347                    bad = mname;
1348                }
1349                /* We don't catch SecurityException since it shouldn't
1350                 * happen for a method in Object and if it does we would
1351                 * like to know about it rather than mysteriously complaining.
1352                 */

1353            }
1354        if (bad != null)
1355        return "contains methods other than getters (" + bad + ")";
1356        return null; // success!
1357
}
1358
1359    final Object JavaDoc fromCompositeData(MXBeanLookup lookup, CompositeData JavaDoc cd,
1360                                 String JavaDoc[] itemNames,
1361                                 OpenConverter[] converters) {
1362        final Class JavaDoc targetClass = getTargetClass();
1363        return
1364        Proxy.newProxyInstance(targetClass.getClassLoader(),
1365                       new Class JavaDoc[] {targetClass},
1366                       new CompositeDataInvocationHandler JavaDoc(cd));
1367    }
1368    }
1369
1370    static InvalidObjectException JavaDoc invalidObjectException(String JavaDoc msg,
1371                                                         Throwable JavaDoc cause) {
1372        return EnvHelp.initCause(new InvalidObjectException JavaDoc(msg), cause);
1373    }
1374    
1375    static InvalidObjectException JavaDoc invalidObjectException(Throwable JavaDoc cause) {
1376        return invalidObjectException(cause.getMessage(), cause);
1377    }
1378    
1379    static OpenDataException JavaDoc openDataException(String JavaDoc msg, Throwable JavaDoc cause) {
1380        return EnvHelp.initCause(new OpenDataException JavaDoc(msg), cause);
1381    }
1382    
1383    static OpenDataException JavaDoc openDataException(Throwable JavaDoc cause) {
1384        return openDataException(cause.getMessage(), cause);
1385    }
1386    
1387    static void mustBeComparable(Class JavaDoc collection, Type JavaDoc element)
1388            throws OpenDataException JavaDoc {
1389        if (!(element instanceof Class JavaDoc)
1390            || !Comparable JavaDoc.class.isAssignableFrom((Class JavaDoc<?>) element)) {
1391            final String JavaDoc msg =
1392                "Parameter class " + element + " of " +
1393                collection.getName() + " does not implement " +
1394                Comparable JavaDoc.class.getName();
1395            throw new OpenDataException JavaDoc(msg);
1396        }
1397    }
1398
1399    /**
1400     * Utility method to take a string and convert it to normal Java variable
1401     * name capitalization. This normally means converting the first
1402     * character from upper case to lower case, but in the (unusual) special
1403     * case when there is more than one character and both the first and
1404     * second characters are upper case, we leave it alone.
1405     * <p>
1406     * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
1407     * as "URL".
1408     *
1409     * @param name The string to be decapitalized.
1410     * @return The decapitalized version of the string.
1411     */

1412    public static String JavaDoc decapitalize(String JavaDoc name) {
1413        if (name == null || name.length() == 0) {
1414            return name;
1415        }
1416        int offset1 = Character.offsetByCodePoints(name, 0, 1);
1417        // Should be name.offsetByCodePoints but 6242664 makes this fail
1418
if (offset1 < name.length() &&
1419                Character.isUpperCase(name.codePointAt(offset1)))
1420            return name;
1421        return name.substring(0, offset1).toLowerCase() +
1422               name.substring(offset1);
1423    }
1424
1425    /**
1426     * Reverse operation for java.beans.Introspector.decapitalize. For any s,
1427     * capitalize(decapitalize(s)).equals(s). The reverse is not true:
1428     * e.g. capitalize("uRL") produces "URL" which is unchanged by
1429     * decapitalize.
1430     */

1431    static String JavaDoc capitalize(String JavaDoc name) {
1432        if (name == null || name.length() == 0)
1433            return name;
1434        int offset1 = name.offsetByCodePoints(0, 1);
1435        return name.substring(0, offset1).toUpperCase() +
1436               name.substring(offset1);
1437    }
1438
1439    public static String JavaDoc propertyName(Method JavaDoc m) {
1440    String JavaDoc rest = null;
1441    String JavaDoc name = m.getName();
1442    if (name.startsWith("get"))
1443        rest = name.substring(3);
1444    else if (name.startsWith("is") && m.getReturnType() == boolean.class)
1445        rest = name.substring(2);
1446    if (rest == null || rest.length() == 0
1447        || m.getParameterTypes().length > 0
1448        || m.getReturnType() == void.class
1449        || name.equals("getClass"))
1450        return null;
1451    return rest;
1452    }
1453
1454    private final static Map JavaDoc<Type JavaDoc, Type JavaDoc> inProgress = newIdentityHashMap();
1455    // really an IdentityHashSet but that doesn't exist
1456
}
1457
Popular Tags