blob: feebdab301dc716ec154c4d003d9305725ace1a8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.management.internal;
import static javax.management.openmbean.SimpleType.BIGDECIMAL;
import static javax.management.openmbean.SimpleType.BIGINTEGER;
import static javax.management.openmbean.SimpleType.BOOLEAN;
import static javax.management.openmbean.SimpleType.BYTE;
import static javax.management.openmbean.SimpleType.CHARACTER;
import static javax.management.openmbean.SimpleType.DATE;
import static javax.management.openmbean.SimpleType.DOUBLE;
import static javax.management.openmbean.SimpleType.FLOAT;
import static javax.management.openmbean.SimpleType.INTEGER;
import static javax.management.openmbean.SimpleType.LONG;
import static javax.management.openmbean.SimpleType.OBJECTNAME;
import static javax.management.openmbean.SimpleType.SHORT;
import static javax.management.openmbean.SimpleType.STRING;
import static javax.management.openmbean.SimpleType.VOID;
import java.beans.ConstructorProperties;
import java.io.InvalidObjectException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;
import javax.management.JMX;
import javax.management.ObjectName;
import javax.management.openmbean.ArrayType;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataInvocationHandler;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.TabularType;
import org.apache.geode.annotations.Immutable;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.management.ManagementException;
/**
* It takes care of converting a Java type to an open types
*
* A Java type is an instance of java.lang.reflect.Type representing all types in Java.
*
* Each Type is associated with an OpenTypeConverter. The OpenTypeConverter defines an OpenType
* corresponding to the Type, plus a Java class corresponding to the OpenType. For example:
*
*
* Java Type : Integer Open Class :Integer Open Type :SimpleType.INTEGER
*
* Apart from simple types, arrays, and collections, Java types are converted through introspection
* into CompositeType
*/
public abstract class OpenTypeConverter {
private final Type targetType;
/**
* The Java class corresponding to getOpenType(). This is the class named by
* getOpenType().getClassName(), except that it may be a primitive type or an array of primitive
* type.
*/
private final OpenType openType;
private final Class openClass;
private static class ConverterMap extends WeakHashMap<Type, WeakReference<OpenTypeConverter>> {
}
@MakeNotStatic
private static final ConverterMap converterMap = new ConverterMap();
@MakeNotStatic
private static final Map<Type, Type> inProgress = OpenTypeUtil.newIdentityHashMap();
/**
* Following List simply serves to keep a reference to predefined OpenConverters so they don't get
* garbage collected.
*/
@MakeNotStatic
private static final List<OpenTypeConverter> preDefinedConverters = OpenTypeUtil.newList();
protected OpenTypeConverter(Type targetType, OpenType openType, Class openClass) {
this.targetType = targetType;
this.openType = openType;
this.openClass = openClass;
}
/**
* Convert an instance of openClass into an instance of targetType.
*
* @return the java type object
*/
public Object fromOpenValue(Object value) throws InvalidObjectException {
if (value == null)
return null;
else
return fromNonNullOpenValue(value);
}
abstract Object fromNonNullOpenValue(Object value) throws InvalidObjectException;
/**
* Throw an appropriate InvalidObjectException if we will not be able to convert back from the
* open data to the original Java object.
*/
void checkReconstructible() throws InvalidObjectException {
// subclasses can override
}
/**
* Convert an instance of targetType into an instance of openClass.
*
* @return open class object
*/
Object toOpenValue(Object value) throws OpenDataException {
if (value == null)
return null;
else
return toNonNullOpenValue(value);
}
abstract Object toNonNullOpenValue(Object value) throws OpenDataException;
/**
* @return True if and only if this OpenTypeConverter's toOpenValue and fromOpenValue methods are
* the identity function.
*/
boolean isIdentity() {
return false;
}
Type getTargetType() {
return targetType;
}
OpenType getOpenType() {
return openType;
}
Class getOpenClass() {
return openClass;
}
/**
* @return a converter corresponding to a type
*/
private static synchronized OpenTypeConverter getConverter(Type type) {
if (type instanceof GenericArrayType) {
Type component = ((GenericArrayType) type).getGenericComponentType();
if (component instanceof Class)
type = Array.newInstance((Class<?>) component, 0).getClass();
}
WeakReference<OpenTypeConverter> wr = converterMap.get(type);
return (wr == null) ? null : wr.get();
}
/**
* Put the converter in the map to avoid future creation
*/
private static synchronized void putConverter(Type type, OpenTypeConverter conv) {
WeakReference<OpenTypeConverter> wr = new WeakReference<OpenTypeConverter>(conv);
converterMap.put(type, wr);
}
private static synchronized void putPreDefinedConverter(Type type, OpenTypeConverter conv) {
putConverter(type, conv);
preDefinedConverters.add(conv);
}
/*
* Static block to initialize pre defined convertor
*/
static {
final OpenType[] simpleTypes = {BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE,
FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING, VOID,};
for (int i = 0; i < simpleTypes.length; i++) {
final OpenType t = simpleTypes[i];
Class c;
try {
c = Class.forName(t.getClassName(), false, ObjectName.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new Error(e);
}
final OpenTypeConverter conv = new IdentityConverter(c, t, c);
putPreDefinedConverter(c, conv);
if (c.getName().startsWith("java.lang.")) {
try {
final Field typeField = c.getField("TYPE");
final Class primitiveType = (Class) typeField.get(null);
final OpenTypeConverter primitiveConv =
new IdentityConverter(primitiveType, t, primitiveType);
putPreDefinedConverter(primitiveType, primitiveConv);
if (primitiveType != void.class) {
final Class primitiveArrayType = Array.newInstance(primitiveType, 0).getClass();
final OpenType primitiveArrayOpenType =
ArrayType.getPrimitiveArrayType(primitiveArrayType);
final OpenTypeConverter primitiveArrayConv = new IdentityConverter(primitiveArrayType,
primitiveArrayOpenType, primitiveArrayType);
putPreDefinedConverter(primitiveArrayType, primitiveArrayConv);
}
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
assert (false);
}
}
}
}
/**
* @return the converter for the given Java type, creating it if necessary
*/
public static synchronized OpenTypeConverter toConverter(Type objType) throws OpenDataException {
if (inProgress.containsKey(objType)) {
throw new OpenDataException("Recursive data structure, including " + typeName(objType));
}
OpenTypeConverter conv;
conv = getConverter(objType);
if (conv != null)
return conv;
inProgress.put(objType, objType);
try {
conv = makeConverter(objType);
} catch (OpenDataException e) {
throw openDataException("Cannot convert type: " + objType, e);
} finally {
inProgress.remove(objType);
}
putConverter(objType, conv);
return conv;
}
/**
* @return the open type converter for a given type
*/
private static OpenTypeConverter makeConverter(Type objType) throws OpenDataException {
if (objType instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) objType).getGenericComponentType();
return makeArrayOrCollectionConverter(objType, componentType);
} else if (objType instanceof Class) {
Class objClass = (Class<?>) objType;
if (objClass.isEnum()) {
return makeEnumConverter(objClass);
} else if (objClass.isArray()) {
Type componentType = objClass.getComponentType();
return makeArrayOrCollectionConverter(objClass, componentType);
} else if (JMX.isMXBeanInterface(objClass)) {
throw openDataException("Cannot obtain array class",
new ManagementException(" MXBean as an Return Type is not supported"));
} else {
return makeCompositeConverter(objClass);
}
} else if (objType instanceof ParameterizedType) {
return makeParameterizedConverter((ParameterizedType) objType);
} else
throw new OpenDataException("Cannot map type: " + objType);
}
private static <T extends Enum<T>> OpenTypeConverter makeEnumConverter(Class<T> enumClass) {
return new EnumConverter<T>(enumClass);
}
private static OpenTypeConverter makeArrayOrCollectionConverter(Type collectionType,
Type elementType) throws OpenDataException {
final OpenTypeConverter elementConverter = toConverter(elementType);
final OpenType elementOpenType = elementConverter.getOpenType();
final ArrayType openType = new ArrayType(1, elementOpenType);
final Class elementOpenClass = elementConverter.getOpenClass();
final Class openArrayClass;
final String openArrayClassName;
if (elementOpenClass.isArray())
openArrayClassName = "[" + elementOpenClass.getName();
else
openArrayClassName = "[L" + elementOpenClass.getName() + ";";
try {
openArrayClass = Class.forName(openArrayClassName);
} catch (ClassNotFoundException e) {
throw openDataException("Cannot obtain array class", e);
}
if (collectionType instanceof ParameterizedType) {
return new CollectionConverter(collectionType, openType, openArrayClass, elementConverter);
} else {
if (elementConverter.isIdentity()) {
return new IdentityConverter(collectionType, openType, openArrayClass);
} else {
return new ArrayConverter(collectionType, openType, openArrayClass, elementConverter);
}
}
}
@Immutable
protected static final String[] keyArray = {"key"};
@Immutable
protected static final String[] keyValueArray = {"key", "value"};
private static OpenTypeConverter makeTabularConverter(Type objType, boolean sortedMap,
Type keyType, Type valueType) throws OpenDataException {
final String objTypeName = objType.toString();
final OpenTypeConverter keyConverter = toConverter(keyType);
final OpenTypeConverter valueConverter = toConverter(valueType);
final OpenType keyOpenType = keyConverter.getOpenType();
final OpenType valueOpenType = valueConverter.getOpenType();
final CompositeType rowType = new CompositeType(objTypeName, objTypeName, keyValueArray,
keyValueArray, new OpenType[] {keyOpenType, valueOpenType});
final TabularType tabularType = new TabularType(objTypeName, objTypeName, rowType, keyArray);
return new TableConverter(objType, sortedMap, tabularType, keyConverter, valueConverter);
}
/**
* Supported types are List<E>, Set<E>, SortedSet<E>, Map<K,V>, SortedMap<K,V>.
*
* Subclasses of the above types wont be supported as deserialize info wont be there.
*
* Queue<E> won't be supported as Queue is more of a functional data structure rather than a data
* holder
*
* @return the open type converter for a given type
*/
private static OpenTypeConverter makeParameterizedConverter(ParameterizedType objType)
throws OpenDataException {
final Type rawType = objType.getRawType();
if (rawType instanceof Class) {
Class c = (Class<?>) rawType;
if (c == List.class || c == Set.class || c == SortedSet.class) {
Type[] actuals = ((ParameterizedType) objType).getActualTypeArguments();
assert (actuals.length == 1);
if (c == SortedSet.class)
mustBeComparable(c, actuals[0]);
return makeArrayOrCollectionConverter(objType, actuals[0]);
} else {
boolean sortedMap = (c == SortedMap.class);
if (c == Map.class || sortedMap) {
Type[] actuals = ((ParameterizedType) objType).getActualTypeArguments();
assert (actuals.length == 2);
if (sortedMap)
mustBeComparable(c, actuals[0]);
return makeTabularConverter(objType, sortedMap, actuals[0], actuals[1]);
}
}
}
throw new OpenDataException("Cannot convert type: " + objType);
}
/**
* @return the open type converrter for a given type
*/
private static OpenTypeConverter makeCompositeConverter(Class c) throws OpenDataException {
final List<Method> methods = Arrays.asList(c.getMethods());
final SortedMap<String, Method> getterMap = OpenTypeUtil.newSortedMap();
for (Method method : methods) {
final String propertyName = propertyName(method);
if (propertyName == null)
continue;
Method old = getterMap.put(OpenTypeUtil.toCamelCase(propertyName), method);
if (old != null) {
final String msg = "Class " + c.getName() + " has method name clash: " + old.getName()
+ ", " + method.getName();
throw new OpenDataException(msg);
}
}
final int nitems = getterMap.size();
if (nitems == 0) {
throw new OpenDataException("Can't map " + c.getName() + " to an open data type");
}
final Method[] getters = new Method[nitems];
final String[] itemNames = new String[nitems];
final OpenType[] openTypes = new OpenType[nitems];
int i = 0;
for (Map.Entry<String, Method> entry : getterMap.entrySet()) {
itemNames[i] = entry.getKey();
final Method getter = entry.getValue();
getters[i] = getter;
final Type retType = getter.getGenericReturnType();
openTypes[i] = toConverter(retType).getOpenType();
i++;
}
CompositeType compositeType = new CompositeType(c.getName(), c.getName(), itemNames, // field
// names
itemNames, // field descriptions
openTypes);
return new CompositeConverter(c, compositeType, itemNames, getters);
}
/**
* Converts from a CompositeData to an instance of the targetClass Various subclasses override its
* functionality.
*/
protected abstract static class CompositeBuilder {
CompositeBuilder(Class targetClass, String[] itemNames) {
this.targetClass = targetClass;
this.itemNames = itemNames;
}
Class getTargetClass() {
return targetClass;
}
String[] getItemNames() {
return itemNames;
}
/**
* If the subclass should be appropriate but there is a problem, then the method throws
* InvalidObjectException.
*
* @return If the subclass is appropriate for targetClass, then the method returns null. If the
* subclass is not appropriate, then the method returns an explanation of why not.
*/
abstract String applicable(Method[] getters) throws InvalidObjectException;
/**
*
* @return possible cause if target class is not applicable
*/
Throwable possibleCause() {
return null;
}
/**
* @return Actual java types from the composite type
*/
abstract Object fromCompositeData(CompositeData cd, String[] itemNames,
OpenTypeConverter[] converters) throws InvalidObjectException;
private final Class targetClass;
private final String[] itemNames;
}
/**
* Builder if the target class has a method "public static from(CompositeData)"
*/
protected static class CompositeBuilderViaFrom extends CompositeBuilder {
CompositeBuilderViaFrom(Class targetClass, String[] itemNames) {
super(targetClass, itemNames);
}
@Override
String applicable(Method[] getters) throws InvalidObjectException {
// See if it has a method "T from(CompositeData)"
// as is conventional for a CompositeDataView
Class targetClass = getTargetClass();
try {
Method fromMethod = targetClass.getMethod("from", new Class[] {CompositeData.class});
if (!Modifier.isStatic(fromMethod.getModifiers())) {
final String msg = "Method from(CompositeData) is not static";
throw new InvalidObjectException(msg);
}
if (fromMethod.getReturnType() != getTargetClass()) {
final String msg = "Method from(CompositeData) returns "
+ typeName(fromMethod.getReturnType()) + " not " + typeName(targetClass);
throw new InvalidObjectException(msg);
}
this.fromMethod = fromMethod;
return null;
} catch (InvalidObjectException e) {
throw e;
} catch (Exception e) {
return "no method from(CompositeData)";
}
}
@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters)
throws InvalidObjectException {
try {
return fromMethod.invoke(null, cd);
} catch (Exception e) {
final String msg = "Failed to invoke from(CompositeData)";
throw invalidObjectException(msg, e);
}
}
private Method fromMethod;
}
/**
* This builder never actually returns success. It simply serves to check whether the other
* builders in the same group have any chance of success. If any getter in the targetClass returns
* a type that we don't know how to reconstruct, then we will not be able to make a builder, and
* there is no point in repeating the error about the problematic getter as many times as there
* are candidate builders. Instead, the "applicable" method will return an explanatory string, and
* the other builders will be skipped. If all the getters are OK, then the "applicable" method
* will return an empty string and the other builders will be tried.
*/
protected static class CompositeBuilderCheckGetters extends CompositeBuilder {
CompositeBuilderCheckGetters(Class targetClass, String[] itemNames,
OpenTypeConverter[] getterConverters) {
super(targetClass, itemNames);
this.getterConverters = getterConverters;
}
@Override
String applicable(Method[] getters) {
for (int i = 0; i < getters.length; i++) {
try {
getterConverters[i].checkReconstructible();
} catch (InvalidObjectException e) {
possibleCause = e;
return "method " + getters[i].getName() + " returns type "
+ "that cannot be mapped back from OpenData";
}
}
return "";
}
@Override
Throwable possibleCause() {
return possibleCause;
}
@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) {
throw new Error();
}
private final OpenTypeConverter[] getterConverters;
private Throwable possibleCause;
}
/**
* Builder if the target class has a setter for every getter
*/
protected static class CompositeBuilderViaSetters extends CompositeBuilder {
CompositeBuilderViaSetters(Class targetClass, String[] itemNames) {
super(targetClass, itemNames);
}
@Override
String applicable(Method[] getters) {
try {
Constructor c = getTargetClass().getConstructor((Class[]) null);
} catch (Exception e) {
return "does not have a public no-arg constructor";
}
Method[] setters = new Method[getters.length];
for (int i = 0; i < getters.length; i++) {
Method getter = getters[i];
Class returnType = getter.getReturnType();
String name = propertyName(getter);
String setterName = "set" + name;
Method setter;
try {
setter = getTargetClass().getMethod(setterName, returnType);
if (setter.getReturnType() != void.class)
throw new Exception();
} catch (Exception e) {
return "not all getters have corresponding setters " + "(" + getter + ")";
}
setters[i] = setter;
}
this.setters = setters;
return null;
}
@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters)
throws InvalidObjectException {
Object o;
try {
o = getTargetClass().newInstance();
for (int i = 0; i < itemNames.length; i++) {
if (cd.containsKey(itemNames[i])) {
Object openItem = cd.get(itemNames[i]);
Object javaItem = converters[i].fromOpenValue(openItem);
setters[i].invoke(o, javaItem);
}
}
} catch (Exception e) {
throw invalidObjectException(e);
}
return o;
}
private Method[] setters;
}
/**
* Builder if the target class has a constructor that is annotated with @ConstructorProperties so
* we can derive the corresponding getters.
*/
protected static class CompositeBuilderViaConstructor extends CompositeBuilder {
CompositeBuilderViaConstructor(Class targetClass, String[] itemNames) {
super(targetClass, itemNames);
}
@Override
String applicable(Method[] getters) throws InvalidObjectException {
final Class<ConstructorProperties> propertyNamesClass = ConstructorProperties.class;
Class targetClass = getTargetClass();
Constructor[] constrs = targetClass.getConstructors();
List<Constructor> annotatedConstrList = OpenTypeUtil.newList();
for (Constructor constr : constrs) {
if (Modifier.isPublic(constr.getModifiers())
&& constr.getAnnotation(propertyNamesClass) != null)
annotatedConstrList.add(constr);
}
if (annotatedConstrList.isEmpty())
return "no constructor has @ConstructorProperties annotation";
annotatedConstructors = OpenTypeUtil.newList();
Map<String, Integer> getterMap = OpenTypeUtil.newMap();
String[] itemNames = getItemNames();
for (int i = 0; i < itemNames.length; i++)
getterMap.put(itemNames[i], i);
Set<BitSet> getterIndexSets = OpenTypeUtil.newSet();
for (Constructor constr : annotatedConstrList) {
String[] propertyNames =
((ConstructorProperties) constr.getAnnotation(propertyNamesClass)).value();
Type[] paramTypes = constr.getGenericParameterTypes();
if (paramTypes.length != propertyNames.length) {
final String msg = "Number of constructor params does not match "
+ "@ConstructorProperties annotation: " + constr;
throw new InvalidObjectException(msg);
}
for (int i = 0; i < paramTypes.length; i++)
paramTypes[i] = fixType(paramTypes[i]);
int[] paramIndexes = new int[getters.length];
for (int i = 0; i < getters.length; i++)
paramIndexes[i] = -1;
BitSet present = new BitSet();
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
if (!getterMap.containsKey(propertyName)) {
String msg = "@ConstructorProperties includes name " + propertyName
+ " which does not correspond to a property";
for (String getterName : getterMap.keySet()) {
if (getterName.equalsIgnoreCase(propertyName)) {
msg += " (differs only in case from property " + getterName + ")";
}
}
msg += ": " + constr;
throw new InvalidObjectException(msg);
}
int getterIndex = getterMap.get(propertyName);
paramIndexes[getterIndex] = i;
if (present.get(getterIndex)) {
final String msg = "@ConstructorProperties contains property " + propertyName
+ " more than once: " + constr;
throw new InvalidObjectException(msg);
}
present.set(getterIndex);
Method getter = getters[getterIndex];
Type propertyType = getter.getGenericReturnType();
if (!propertyType.equals(paramTypes[i])) {
final String msg = "@ConstructorProperties gives property " + propertyName + " of type "
+ propertyType + " for parameter " + " of type " + paramTypes[i] + ": " + constr;
throw new InvalidObjectException(msg);
}
}
if (!getterIndexSets.add(present)) {
final String msg = "More than one constructor has a @ConstructorProperties "
+ "annotation with this set of names: " + Arrays.toString(propertyNames);
throw new InvalidObjectException(msg);
}
Constr c = new Constr(constr, paramIndexes, present);
annotatedConstructors.add(c);
}
for (BitSet a : getterIndexSets) {
boolean seen = false;
for (BitSet b : getterIndexSets) {
if (a == b)
seen = true;
else if (seen) {
BitSet u = new BitSet();
u.or(a);
u.or(b);
if (!getterIndexSets.contains(u)) {
Set<String> names = new TreeSet<String>();
for (int i = u.nextSetBit(0); i >= 0; i = u.nextSetBit(i + 1))
names.add(itemNames[i]);
final String msg = "Constructors with @ConstructorProperties annotation "
+ " would be ambiguous for these items: " + names;
throw new InvalidObjectException(msg);
}
}
}
}
return null;
}
@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters)
throws InvalidObjectException {
CompositeType ct = cd.getCompositeType();
BitSet present = new BitSet();
for (int i = 0; i < itemNames.length; i++) {
if (ct.getType(itemNames[i]) != null)
present.set(i);
}
Constr max = null;
for (Constr constr : annotatedConstructors) {
if (subset(constr.presentParams, present)
&& (max == null || subset(max.presentParams, constr.presentParams)))
max = constr;
}
if (max == null) {
final String msg = "No constructor has a @ConstructorProperties for this set of "
+ "items: " + ct.keySet();
throw new InvalidObjectException(msg);
}
Object[] params = new Object[max.presentParams.cardinality()];
for (int i = 0; i < itemNames.length; i++) {
if (!max.presentParams.get(i))
continue;
Object openItem = cd.get(itemNames[i]);
Object javaItem = converters[i].fromOpenValue(openItem);
int index = max.paramIndexes[i];
if (index >= 0)
params[index] = javaItem;
}
try {
return max.constructor.newInstance(params);
} catch (Exception e) {
final String msg = "Exception constructing " + getTargetClass().getName();
throw invalidObjectException(msg, e);
}
}
private static boolean subset(BitSet sub, BitSet sup) {
BitSet subcopy = (BitSet) sub.clone();
subcopy.andNot(sup);
return subcopy.isEmpty();
}
private static class Constr {
final Constructor constructor;
final int[] paramIndexes;
final BitSet presentParams;
Constr(Constructor constructor, int[] paramIndexes, BitSet presentParams) {
this.constructor = constructor;
this.paramIndexes = paramIndexes;
this.presentParams = presentParams;
}
}
private List<Constr> annotatedConstructors;
}
/**
* Builder if the target class is an interface and contains no methods other than getters. Then we
* can make an instance using a dynamic proxy that forwards the getters to the source
* CompositeData
*/
protected static class CompositeBuilderViaProxy extends CompositeBuilder {
CompositeBuilderViaProxy(Class targetClass, String[] itemNames) {
super(targetClass, itemNames);
}
@Override
String applicable(Method[] getters) {
Class targetClass = getTargetClass();
if (!targetClass.isInterface())
return "not an interface";
Set<Method> methods = OpenTypeUtil.newSet(Arrays.asList(targetClass.getMethods()));
methods.removeAll(Arrays.asList(getters));
String bad = null;
for (Method m : methods) {
String mname = m.getName();
Class[] mparams = m.getParameterTypes();
try {
Method om = Object.class.getMethod(mname, mparams);
if (!Modifier.isPublic(om.getModifiers()))
bad = mname;
} catch (NoSuchMethodException e) {
bad = mname;
}
}
if (bad != null)
return "contains methods other than getters (" + bad + ")";
return null;
}
@Override
Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) {
final Class targetClass = getTargetClass();
return Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[] {targetClass},
new CompositeDataInvocationHandler(cd));
}
}
static InvalidObjectException invalidObjectException(String msg, Throwable cause) {
return new InvalidObjectException(msg);
}
static InvalidObjectException invalidObjectException(Throwable cause) {
return invalidObjectException(cause.getMessage(), cause);
}
static OpenDataException openDataException(String msg, Throwable cause) {
return new OpenDataException(msg);
}
static OpenDataException openDataException(Throwable cause) {
return openDataException(cause.getMessage(), cause);
}
static void mustBeComparable(Class collection, Type element) throws OpenDataException {
if (!(element instanceof Class) || !Comparable.class.isAssignableFrom((Class<?>) element)) {
final String msg = "Parameter class " + element + " of " + collection.getName()
+ " does not implement " + Comparable.class.getName();
throw new OpenDataException(msg);
}
}
public static String propertyName(Method m) {
String rest = null;
String name = m.getName();
if (name.startsWith("get"))
rest = name.substring(3);
else if (name.startsWith("is") && m.getReturnType() == boolean.class)
rest = name.substring(2);
if (rest == null || rest.length() == 0 || m.getParameterTypes().length > 0
|| m.getReturnType() == void.class || name.equals("getClass"))
return null;
return rest;
}
protected static String typeName(Type t) {
if (t instanceof Class<?>) {
Class<?> c = (Class<?>) t;
if (c.isArray())
return typeName(c.getComponentType()) + "[]";
else
return c.getName();
}
return t.toString();
}
private static Type fixType(Type t) {
if (!(t instanceof GenericArrayType))
return t;
GenericArrayType gat = (GenericArrayType) t;
Type ultimate = ultimateComponentType(gat);
if (!(ultimate instanceof Class<?>))
return t;
Class<?> component = (Class<?>) fixType(gat.getGenericComponentType());
return Array.newInstance(component, 0).getClass();
}
private static Type ultimateComponentType(GenericArrayType gat) {
Type component = gat.getGenericComponentType();
if (component instanceof GenericArrayType)
return ultimateComponentType((GenericArrayType) component);
else
return component;
}
}