blob: 49f6493fbc7071ce776afca28da8a448c0bc4f1d [file] [log] [blame]
/*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @bug 5024531
* @summary Utility class to convert a struct-like class to a CompositeData.
* @author Mandy Chung
*/
import java.lang.reflect.*;
import java.util.*;
import javax.management.*;
import javax.management.openmbean.*;
import static javax.management.openmbean.SimpleType.*;
/**
* A converter utiltiy class to automatically convert a given
* class to a CompositeType.
*/
public class OpenTypeConverter {
private static final WeakHashMap<Class,OpenType> convertedTypes =
new WeakHashMap<Class,OpenType>();
private static final OpenType[] simpleTypes = {
BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE,
DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING,
VOID,
};
static {
for (int i = 0; i < simpleTypes.length; i++) {
final OpenType t = simpleTypes[i];
Class c;
try {
c = Class.forName(t.getClassName(), false,
String.class.getClassLoader());
} catch (ClassNotFoundException e) {
// the classes that these predefined types declare must exist!
assert(false);
c = null; // not reached
}
convertedTypes.put(c, t);
if (c.getName().startsWith("java.lang.")) {
try {
final Field typeField = c.getField("TYPE");
final Class primitiveType = (Class) typeField.get(null);
convertedTypes.put(primitiveType, t);
} catch (NoSuchFieldException e) {
// OK: must not be a primitive wrapper
} catch (IllegalAccessException e) {
// Should not reach here
throw new AssertionError(e);
}
}
}
}
private static class InProgress extends OpenType {
private static final String description =
"Marker to detect recursive type use -- internal use only!";
InProgress() throws OpenDataException {
super("java.lang.String", "java.lang.String", description);
}
public String toString() {
return description;
}
public int hashCode() {
return 0;
}
public boolean equals(Object o) {
return false;
}
public boolean isValue(Object o) {
return false;
}
}
private static final OpenType inProgress;
static {
OpenType t;
try {
t = new InProgress();
} catch (OpenDataException e) {
// Should not reach here
throw new AssertionError(e);
}
inProgress = t;
}
// Convert a class to an OpenType
public static synchronized OpenType toOpenType(Class c)
throws OpenDataException {
OpenType t;
t = convertedTypes.get(c);
if (t != null) {
if (t instanceof InProgress)
throw new OpenDataException("Recursive data structure");
return t;
}
convertedTypes.put(c, inProgress);
if (Enum.class.isAssignableFrom(c))
t = STRING;
else if (c.isArray())
t = makeArrayType(c);
else
t = makeCompositeType(c);
convertedTypes.put(c, t);
return t;
}
private static OpenType makeArrayType(Class c) throws OpenDataException {
int dim;
for (dim = 0; c.isArray(); dim++)
c = c.getComponentType();
return new ArrayType(dim, toOpenType(c));
}
private static OpenType makeCompositeType(Class c)
throws OpenDataException {
// Make a CompositeData containing all the getters
final Method[] methods = c.getMethods();
final List<String> names = new ArrayList<String>();
final List<OpenType> types = new ArrayList<OpenType>();
/* Select public methods that look like "T getX()" or "boolean
isX() or hasX()", where T is not void and X is not the empty
string. Exclude "Class getClass()" inherited from Object. */
for (int i = 0; i < methods.length; i++) {
final Method method = methods[i];
final String name = method.getName();
final Class type = method.getReturnType();
final String rest;
if (name.startsWith("get"))
rest = name.substring(3);
else if (name.startsWith("is") && type == boolean.class)
rest = name.substring(2);
else if (name.startsWith("has") && type == boolean.class)
rest = name.substring(3);
else
continue;
if (rest.equals("") || method.getParameterTypes().length > 0
|| type == void.class || rest.equals("Class"))
continue;
names.add(decapitalize(rest));
types.add(toOpenType(type));
}
final String[] nameArray = names.toArray(new String[0]);
return new CompositeType(c.getName(),
c.getName(),
nameArray, // field names
nameArray, // field descriptions
types.toArray(new OpenType[0]));
}
/**
* Utility method to take a string and convert it to normal Java variable
* name capitalization. This normally means converting the first
* character from upper case to lower case, but in the (unusual) special
* case when there is more than one character and both the first and
* second characters are upper case, we leave it alone.
* <p>
* Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
* as "URL".
*
* @param name The string to be decapitalized.
* @return The decapitalized version of the string.
*/
private static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
}