📄 openconverter.java
字号:
public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object openValue) throws InvalidObjectException { final TabularData table = (TabularData) openValue; final Collection<CompositeData> rows = (Collection<CompositeData>) table.values(); final Map<Object, Object> valueMap = sortedMap ? newSortedMap() : newMap(); for (CompositeData row : rows) { final Object key = keyConverter.fromOpenValue(lookup, row.get("key")); final Object value = valueConverter.fromOpenValue(lookup, row.get("value")); if (valueMap.put(key, value) != null) { final String msg = "Duplicate entry in TabularData: key=" + key; throw new InvalidObjectException(msg); } } return valueMap; } void checkReconstructible() throws InvalidObjectException { keyConverter.checkReconstructible(); valueConverter.checkReconstructible(); } private final boolean sortedMap; private final OpenConverter keyConverter; private final OpenConverter valueConverter; } private static final class CompositeConverter extends OpenConverter { CompositeConverter(Class targetClass, CompositeType compositeType, String[] itemNames, Method[] getters) throws OpenDataException { super(targetClass, compositeType, CompositeData.class); assert(itemNames.length == getters.length); this.itemNames = itemNames; this.getters = getters; this.getterConverters = new OpenConverter[getters.length]; for (int i = 0; i < getters.length; i++) { Type retType = getters[i].getGenericReturnType(); getterConverters[i] = OpenConverter.toConverter(retType); } } final Object toNonNullOpenValue(MXBeanLookup lookup, Object value) throws OpenDataException { CompositeType ct = (CompositeType) getOpenType(); if (value instanceof CompositeDataView) return ((CompositeDataView) value).toCompositeData(ct); if (value == null) return null; Object[] values = new Object[getters.length]; for (int i = 0; i < getters.length; i++) { try { Object got = getters[i].invoke(value, (Object[]) null); values[i] = getterConverters[i].toOpenValue(lookup, got); } catch (Exception e) { throw openDataException("Error calling getter for " + itemNames[i] + ": " + e, e); } } return new CompositeDataSupport(ct, itemNames, values); } /** Determine how to convert back from the CompositeData into the original Java type. For a type that is not reconstructible, this method will fail every time, and will throw the right exception. */ private synchronized void makeCompositeBuilder() throws InvalidObjectException { if (compositeBuilder != null) return; Class targetClass = (Class<?>) getTargetType(); /* In this 2D array, each subarray is a set of builders where there is no point in consulting the ones after the first if the first refuses. */ CompositeBuilder[][] builders = { { new CompositeBuilderViaFrom(targetClass, itemNames), }, { new CompositeBuilderViaConstructor(targetClass, itemNames), }, { new CompositeBuilderCheckGetters(targetClass, itemNames, getterConverters), new CompositeBuilderViaSetters(targetClass, itemNames), new CompositeBuilderViaProxy(targetClass, itemNames), }, }; CompositeBuilder foundBuilder = null; /* We try to make a meaningful exception message by concatenating each Builder's explanation of why it isn't applicable. */ StringBuilder whyNots = new StringBuilder(); Throwable possibleCause = null; find: for (CompositeBuilder[] relatedBuilders : builders) { for (int i = 0; i < relatedBuilders.length; i++) { CompositeBuilder builder = relatedBuilders[i]; String whyNot = builder.applicable(getters); if (whyNot == null) { foundBuilder = builder; break find; } Throwable cause = builder.possibleCause(); if (cause != null) possibleCause = cause; if (whyNot.length() > 0) { if (whyNots.length() > 0) whyNots.append("; "); whyNots.append(whyNot); if (i == 0) break; // skip other builders in this group } } } if (foundBuilder == null) { String msg = "Do not know how to make a " + targetClass.getName() + " from a CompositeData: " + whyNots; if (possibleCause != null) msg += ". Remaining exceptions show a POSSIBLE cause."; throw invalidObjectException(msg, possibleCause); } compositeBuilder = foundBuilder; } void checkReconstructible() throws InvalidObjectException { makeCompositeBuilder(); } public final Object fromNonNullOpenValue(MXBeanLookup lookup, Object value) throws InvalidObjectException { makeCompositeBuilder(); return compositeBuilder.fromCompositeData(lookup, (CompositeData) value, itemNames, getterConverters); } private final String[] itemNames; private final Method[] getters; private final OpenConverter[] getterConverters; private CompositeBuilder compositeBuilder; } /** Converts from a CompositeData to an instance of the targetClass. */ private static abstract class CompositeBuilder { CompositeBuilder(Class targetClass, String[] itemNames) { this.targetClass = targetClass; this.itemNames = itemNames; } Class getTargetClass() { return targetClass; } String[] getItemNames() { return itemNames; } /** 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. If the subclass should be appropriate but there is a problem, then the method throws InvalidObjectException. */ abstract String applicable(Method[] getters) throws InvalidObjectException; /** If the subclass returns an explanation of why it is not applicable, it can additionally indicate an exception with details. This is potentially confusing, because the real problem could be that one of the other subclasses is supposed to be applicable but isn't. But the advantage of less information loss probably outweighs the disadvantage of possible confusion. */ Throwable possibleCause() { return null; } abstract Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] converters) throws InvalidObjectException; private final Class targetClass; private final String[] itemNames; } /** Builder for when the target class has a method "public static from(CompositeData)". */ private static final class CompositeBuilderViaFrom extends CompositeBuilder { CompositeBuilderViaFrom(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } 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; // success! } catch (InvalidObjectException e) { throw e; } catch (Exception e) { // OK: it doesn't have the method return "no method from(CompositeData)"; } } final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] 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. */ private static class CompositeBuilderCheckGetters extends CompositeBuilder { CompositeBuilderCheckGetters(Class targetClass, String[] itemNames, OpenConverter[] getterConverters) { super(targetClass, itemNames); this.getterConverters = getterConverters; } 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; } final Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] converters) { throw new Error(); } private final OpenConverter[] getterConverters; private Throwable possibleCause; } /** Builder for when the target class has a setter for every getter. */ private static class CompositeBuilderViaSetters extends CompositeBuilder { CompositeBuilderViaSetters(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } 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; } Object fromCompositeData(MXBeanLookup lookup, CompositeData cd, String[] itemNames, OpenConverter[] 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(lookup, openItem); setters[i].invoke(o, javaItem); } } } catch (Exception e) { throw invalidObjectException(e); } return o; } private Method[] setters; } /** Builder for when the target class has a constructor that is annotated with @ConstructorProperties so we can see the correspondence to getters. */ private static final class CompositeBuilderViaConstructor extends CompositeBuilder { CompositeBuilderViaConstructor(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) throws InvalidObjectException { final Class<ConstructorProperties> propertyNamesClass = ConstructorProperties.class; Class targetClass = getTargetClass(); Constructor[] constrs = targetClass.getConstructors(); // Applicable if and only if there are any annotated constructors List<Constructor> annotatedConstrList = newList(); for (Constructor constr : constrs) { if (Modifier.isPublic(constr.getModifiers()) && constr.getAnnotation(propertyNamesClass) != null) annotatedConstrList.add(constr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -