📄 openconverter.java
字号:
} if (annotatedConstrList.isEmpty()) return "no constructor has @ConstructorProperties annotation"; annotatedConstructors = newList(); // Now check that all the annotated constructors are valid // and throw an exception if not. // First link the itemNames to their getter indexes. Map<String, Integer> getterMap = newMap(); String[] itemNames = getItemNames(); for (int i = 0; i < itemNames.length; i++) getterMap.put(itemNames[i], i); // Run through the constructors making the checks in the spec. // For each constructor, remember the correspondence between its // parameters and the items. The int[] for a constructor says // what parameter index should get what item. For example, // if element 0 is 2 then that means that item 0 in the // CompositeData goes to parameter 2 of the constructor. If an // element is -1, that item isn't given to the constructor. // Also remember the set of properties in that constructor // so we can test unambiguity. Set<BitSet> getterIndexSets = newSet(); for (Constructor constr : annotatedConstrList) { String[] propertyNames = 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); } // Work around bug 6710498 (really caused by 5041784): 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); } /* Check that no possible set of items could lead to an ambiguous * choice of constructor (spec requires this check). For any * pair of constructors, their union would be the minimal * ambiguous set. If this set itself corresponds to a constructor, * there is no ambiguity for that pair. In the usual case, one * of the constructors is a superset of the other so the union is * just the bigger constuctor. * * The algorithm here is quadratic in the number of constructors * with a @ConstructorProperties annotation. Typically this corresponds * to the number of versions of the class there have been. Ten * would already be a large number, so although it's probably * possible to have an O(n lg n) algorithm it wouldn't be * worth the complexity. */ 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; // success! } Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] converters) throws InvalidObjectException { // The CompositeData might come from an earlier version where // not all the items were present. We look for a constructor // that accepts just the items that are present. Because of // the ambiguity check in applicable(), we know there must be // at most one maximally applicable constructor. 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(lookup, 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 for when 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. */ private static final class CompositeBuilderViaProxy extends CompositeBuilder { CompositeBuilderViaProxy(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) { Class targetClass = getTargetClass(); if (!targetClass.isInterface()) return "not an interface"; Set<Method> methods = newSet(Arrays.asList(targetClass.getMethods())); methods.removeAll(Arrays.asList(getters)); /* If the interface has any methods left over, they better be * public methods that are already present in java.lang.Object. */ 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; } /* We don't catch SecurityException since it shouldn't * happen for a method in Object and if it does we would * like to know about it rather than mysteriously complaining. */ } if (bad != null) return "contains methods other than getters (" + bad + ")"; return null; // success! } final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] converters) { final Class targetClass = getTargetClass(); return Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[] {targetClass}, new CompositeDataInvocationHandler(cd)); } } static InvalidObjectException invalidObjectException(String msg, Throwable cause) { return EnvHelp.initCause(new InvalidObjectException(msg), cause); } static InvalidObjectException invalidObjectException(Throwable cause) { return invalidObjectException(cause.getMessage(), cause); } static OpenDataException openDataException(String msg, Throwable cause) { return EnvHelp.initCause(new OpenDataException(msg), cause); } 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); } } /** * 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. */ public static String decapitalize(String name) { if (name == null || name.length() == 0) { return name; } int offset1 = Character.offsetByCodePoints(name, 0, 1); // Should be name.offsetByCodePoints but 6242664 makes this fail if (offset1 < name.length() && Character.isUpperCase(name.codePointAt(offset1))) return name; return name.substring(0, offset1).toLowerCase() + name.substring(offset1); } /** * Reverse operation for java.beans.Introspector.decapitalize. For any s, * capitalize(decapitalize(s)).equals(s). The reverse is not true: * e.g. capitalize("uRL") produces "URL" which is unchanged by * decapitalize. */ static String capitalize(String name) { if (name == null || name.length() == 0) return name; int offset1 = name.offsetByCodePoints(0, 1); return name.substring(0, offset1).toUpperCase() + name.substring(offset1); } 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; } private 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(); } // Work around for bug 5041784, where types like String[] are sometimes // represented as GenericArrayType instead of Class. 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; } private final static Map<Type, Type> inProgress = newIdentityHashMap(); // really an IdentityHashSet but that doesn't exist}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -