summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon')
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableGenerator.java46
-rw-r--r--src/main/java/com/amazon/carbonado/info/StorableIntrospector.java110
-rw-r--r--src/main/java/com/amazon/carbonado/info/StorableProperty.java10
-rw-r--r--src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java4
4 files changed, 163 insertions, 7 deletions
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<S extends Storable> {
b.returnVoid();
}
}
+
+ addPropertyBridges(property);
}
}
@@ -1760,6 +1762,50 @@ public final class StorableGenerator<S extends Storable> {
}
}
+ private void addPropertyBridges(StorableProperty<S> 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();
}
}
@@ -753,6 +765,51 @@ public class StorableIntrospector {
}
/**
+ * @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
* it. Put another way, the upper bound is Storable, and the lower bound
@@ -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<S> 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,11 +45,19 @@ public interface StorableProperty<S extends Storable> 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();
}