blob: 8475eb8f790aeceba1f3bb721b13ed9158705438 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @bug 5024531
26 * @summary Utility class to convert a struct-like class to a CompositeData.
27 * @author Mandy Chung
28 */
29
30import java.lang.reflect.*;
31import java.util.*;
32import javax.management.*;
33import javax.management.openmbean.*;
34import static javax.management.openmbean.SimpleType.*;
35
36/**
37 * A converter utiltiy class to automatically convert a given
38 * class to a CompositeType.
39 */
40public class OpenTypeConverter {
41 private static final WeakHashMap<Class,OpenType> convertedTypes =
42 new WeakHashMap<Class,OpenType>();
43 private static final OpenType[] simpleTypes = {
44 BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
45 DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
46 VOID,
47 };
48
49 static {
50 for (int i = 0; i < simpleTypes.length; i++) {
51 final OpenType t = simpleTypes[i];
52 Class c;
53 try {
54 c = Class.forName(t.getClassName(), false,
55 String.class.getClassLoader());
56 } catch (ClassNotFoundException e) {
57 // the classes that these predefined types declare must exist!
58 assert(false);
59 c = null; // not reached
60 }
61 convertedTypes.put(c, t);
62
63 if (c.getName().startsWith("java.lang.")) {
64 try {
65 final Field typeField = c.getField("TYPE");
66 final Class primitiveType = (Class) typeField.get(null);
67 convertedTypes.put(primitiveType, t);
68 } catch (NoSuchFieldException e) {
69 // OK: must not be a primitive wrapper
70 } catch (IllegalAccessException e) {
71 // Should not reach here
72 throw new AssertionError(e);
73 }
74 }
75 }
76 }
77
78 private static class InProgress extends OpenType {
79 private static final String description =
80 "Marker to detect recursive type use -- internal use only!";
81
82 InProgress() throws OpenDataException {
83 super("java.lang.String", "java.lang.String", description);
84 }
85
86 public String toString() {
87 return description;
88 }
89
90 public int hashCode() {
91 return 0;
92 }
93
94 public boolean equals(Object o) {
95 return false;
96 }
97
98 public boolean isValue(Object o) {
99 return false;
100 }
101 }
102 private static final OpenType inProgress;
103 static {
104 OpenType t;
105 try {
106 t = new InProgress();
107 } catch (OpenDataException e) {
108 // Should not reach here
109 throw new AssertionError(e);
110 }
111 inProgress = t;
112 }
113
114 // Convert a class to an OpenType
115 public static synchronized OpenType toOpenType(Class c)
116 throws OpenDataException {
117
118 OpenType t;
119
120 t = convertedTypes.get(c);
121 if (t != null) {
122 if (t instanceof InProgress)
123 throw new OpenDataException("Recursive data structure");
124 return t;
125 }
126
127 convertedTypes.put(c, inProgress);
128
129 if (Enum.class.isAssignableFrom(c))
130 t = STRING;
131 else if (c.isArray())
132 t = makeArrayType(c);
133 else
134 t = makeCompositeType(c);
135
136 convertedTypes.put(c, t);
137
138 return t;
139 }
140
141 private static OpenType makeArrayType(Class c) throws OpenDataException {
142 int dim;
143 for (dim = 0; c.isArray(); dim++)
144 c = c.getComponentType();
145 return new ArrayType(dim, toOpenType(c));
146 }
147
148 private static OpenType makeCompositeType(Class c)
149 throws OpenDataException {
150 // Make a CompositeData containing all the getters
151 final Method[] methods = c.getMethods();
152 final List<String> names = new ArrayList<String>();
153 final List<OpenType> types = new ArrayList<OpenType>();
154
155 /* Select public methods that look like "T getX()" or "boolean
156 isX() or hasX()", where T is not void and X is not the empty
157 string. Exclude "Class getClass()" inherited from Object. */
158 for (int i = 0; i < methods.length; i++) {
159 final Method method = methods[i];
160 final String name = method.getName();
161 final Class type = method.getReturnType();
162 final String rest;
163 if (name.startsWith("get"))
164 rest = name.substring(3);
165 else if (name.startsWith("is") && type == boolean.class)
166 rest = name.substring(2);
167 else if (name.startsWith("has") && type == boolean.class)
168 rest = name.substring(3);
169 else
170 continue;
171
172 if (rest.equals("") || method.getParameterTypes().length > 0
173 || type == void.class || rest.equals("Class"))
174 continue;
175
176 names.add(decapitalize(rest));
177 types.add(toOpenType(type));
178 }
179
180 final String[] nameArray = names.toArray(new String[0]);
181 return new CompositeType(c.getName(),
182 c.getName(),
183 nameArray, // field names
184 nameArray, // field descriptions
185 types.toArray(new OpenType[0]));
186 }
187
188 /**
189 * Utility method to take a string and convert it to normal Java variable
190 * name capitalization. This normally means converting the first
191 * character from upper case to lower case, but in the (unusual) special
192 * case when there is more than one character and both the first and
193 * second characters are upper case, we leave it alone.
194 * <p>
195 * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
196 * as "URL".
197 *
198 * @param name The string to be decapitalized.
199 * @return The decapitalized version of the string.
200 */
201 private static String decapitalize(String name) {
202 if (name == null || name.length() == 0) {
203 return name;
204 }
205 if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
206 Character.isUpperCase(name.charAt(0))){
207 return name;
208 }
209 char chars[] = name.toCharArray();
210 chars[0] = Character.toLowerCase(chars[0]);
211 return new String(chars);
212 }
213
214}