From 6e894b3ebd0c2d6d2c2551ec013ae8db885d7ab4 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sat, 27 Dec 2008 01:40:50 +0000 Subject: Support covariant properties. Cojen must support this feature too in order to work (revision 169). --- .../amazon/carbonado/gen/StorableGenerator.java | 46 +++++++++ .../carbonado/info/StorableIntrospector.java | 110 +++++++++++++++++++-- .../amazon/carbonado/info/StorableProperty.java | 10 +- .../repo/jdbc/JDBCStorableIntrospector.java | 4 + 4 files changed, 163 insertions(+), 7 deletions(-) (limited to 'src/main') diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index d194634..f84748f 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -968,6 +968,8 @@ public final class StorableGenerator { b.returnVoid(); } } + + addPropertyBridges(property); } } @@ -1760,6 +1762,50 @@ public final class StorableGenerator { } } + private void addPropertyBridges(StorableProperty property) { + Class[] covariantTypes = property.getCovariantTypes(); + if (covariantTypes == null || covariantTypes.length == 0) { + return; + } + + // Define copy bridges to allow covariant property types. + + for (Class type : covariantTypes) { + TypeDesc desc = TypeDesc.forClass(type); + + if (property.getReadMethod() != null && + property.getReadMethod().getReturnType() != type) + { + MethodInfo mi = addMethodIfNotFinal + (Modifiers.PUBLIC.toBridge(true), property.getReadMethodName(), desc, null); + + if (mi != null) { + CodeBuilder b = new CodeBuilder(mi); + b.loadThis(); + b.invoke(property.getReadMethod()); + b.returnValue(desc); + } + } + + if (property.getWriteMethod() != null && + property.getWriteMethod().getParameterTypes()[0] != type) + { + // Not actually defined as a bridge method since parameter type differs. + MethodInfo mi = addMethodIfNotFinal + (Modifiers.PUBLIC, property.getWriteMethodName(), null, new TypeDesc[] {desc}); + + if (mi != null) { + CodeBuilder b = new CodeBuilder(mi); + b.loadThis(); + b.loadLocal(b.getParameter(0)); + b.checkCast(TypeDesc.forClass(property.getType())); + b.invoke(property.getWriteMethod()); + b.returnVoid(); + } + } + } + } + /** * Generates a copy properties method with several options to control its * behavior. Although eight combinations can be defined, only four are diff --git a/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java b/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java index 2b9b1fc..34dd088 100644 --- a/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java @@ -26,11 +26,13 @@ import java.lang.annotation.Annotation; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.TypeVariable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -73,6 +75,7 @@ import com.amazon.carbonado.adapter.AdapterDefinition; import com.amazon.carbonado.constraint.ConstraintDefinition; import com.amazon.carbonado.lob.Lob; import com.amazon.carbonado.util.ConversionComparator; +import com.amazon.carbonado.util.ThrowUnchecked; /** * Supports examination of {@link Storable} types, returning all metadata @@ -706,17 +709,26 @@ public class StorableIntrospector { while (iter.hasNext()) { Method m = iter.next(); int methodModifiers = m.getModifiers(); - if (Modifier.isAbstract(methodModifiers )) { + if (Modifier.isAbstract(methodModifiers)) { + String message; + if (!Modifier.isPublic(methodModifiers) && !Modifier.isProtected(methodModifiers)) { - errorMessages.add("Abstract method cannot be defined (neither public or " + - "protected): " + m); + message = "Abstract method cannot be defined " + + "(neither public or protected): "; + } else if (!isCovariant(allProperties, m)) { + message = "Abstract method cannot be defined (not a bean property): "; } else { - errorMessages.add - ("Abstract method cannot be defined (not a bean property): " + m); + message = null; + } + + if (message != null) { + errorMessages.add(message + m); } - // We've reported the error, nothing more to say about it + + // We've reported an error or validated method. No need to + // check it again. iter.remove(); } } @@ -752,6 +764,51 @@ public class StorableIntrospector { return Collections.unmodifiableMap(properties); } + /** + * @param allProperties map of BeanProperty instances + */ + private static boolean isCovariant(Map allProperties, Method m) { + for (Object obj : allProperties.values()) { + BeanProperty property = (BeanProperty) obj; + Class[] covariantTypes = property.getCovariantTypes(); + if (covariantTypes == null || covariantTypes.length == 0) { + continue; + } + + Class returnType = m.getReturnType(); + Class[] paramTypes = m.getParameterTypes(); + Class type; + + if (m.getName().equals(property.getReadMethod().getName())) { + if (returnType == null || returnType == void.class) { + continue; + } + if (paramTypes == null || paramTypes.length > 0) { + continue; + } + type = returnType; + } else if (m.getName().equals(property.getWriteMethod().getName())) { + if (returnType != null && returnType != void.class) { + continue; + } + if (paramTypes == null || paramTypes.length != 1) { + continue; + } + type = paramTypes[0]; + } else { + continue; + } + + for (Class covariantType : covariantTypes) { + if (type == covariantType) { + return true; + } + } + } + + return false; + } + /** * Make sure that the parameter type that is specified to Storable can be * assigned to a Storable, and that the given type can be assigned to @@ -1133,9 +1190,22 @@ public class StorableIntrospector { joinedType = Storable.class; } else { Type arg = args[0]; + + if (arg instanceof WildcardType) { + Type[] upper = ((WildcardType) arg).getUpperBounds(); + // Length should only be one or zero. + if (upper.length == 1) { + arg = upper[0]; + } else { + // Default. + arg = Storable.class; + } + } + while (arg instanceof ParameterizedType) { arg = ((ParameterizedType)arg).getRawType(); } + if (arg instanceof Class) { joinedType = (Class)arg; } @@ -1669,6 +1739,19 @@ public class StorableIntrospector { private static final long serialVersionUID = 6599542401516624863L; private static final ChainedProperty[] EMPTY_CHAIN_ARRAY = new ChainedProperty[0]; + private static final Class[] EMPTY_CLASSES_ARRAY = new Class[0]; + + private static final Method cCovariantTypesMethod; + + static { + Method method; + try { + method = BeanProperty.class.getMethod("getCovariantTypes", (Class[]) null); + } catch (NoSuchMethodException e) { + method = null; + } + cCovariantTypesMethod = method; + } private final BeanProperty mBeanProperty; private final Class mEnclosingType; @@ -1742,6 +1825,21 @@ public class StorableIntrospector { return mBeanProperty.getType(); } + public Class[] getCovariantTypes() { + // Access via reflection since this is a feature not available in + // all versions of Cojen. + if (cCovariantTypesMethod != null) { + try { + return (Class[]) cCovariantTypesMethod.invoke(mBeanProperty, (Object[]) null); + } catch (InvocationTargetException e) { + ThrowUnchecked.fireDeclaredCause(e); + } catch (IllegalAccessException e) { + ThrowUnchecked.fireDeclared(e); + } + } + return EMPTY_CLASSES_ARRAY; + } + public final int getNumber() { return mNumber; } diff --git a/src/main/java/com/amazon/carbonado/info/StorableProperty.java b/src/main/java/com/amazon/carbonado/info/StorableProperty.java index 38418ec..36491f8 100644 --- a/src/main/java/com/amazon/carbonado/info/StorableProperty.java +++ b/src/main/java/com/amazon/carbonado/info/StorableProperty.java @@ -45,10 +45,18 @@ public interface StorableProperty extends Serializable, Appe String getBeanName(); /** - * Returns the type of this property. + * Returns the primary type of this property. */ Class getType(); + /** + * Returns additional types of this property, all of which are assignable + * by the primary type. + * + * @since 1.2.1 + */ + Class[] getCovariantTypes(); + /** * Returns the zero-based numerical position of this property within its * enclosing type. diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java index 0403fbf..66e1ad5 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java @@ -1257,6 +1257,10 @@ public class JDBCStorableIntrospector extends StorableIntrospector { return mMainProperty.getType(); } + public Class[] getCovariantTypes() { + return mMainProperty.getCovariantTypes(); + } + public int getNumber() { return mMainProperty.getNumber(); } -- cgit v1.2.3