1/* 2 * Copyright (c) 2004, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * 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} 215