From 641a7812d439daca3045e7471b604beb807c1891 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 1 Apr 2007 00:00:07 +0000 Subject: Move Storable code generation support to separate package. --- .../carbonado/info/ConversionComparator.java | 212 +++++++++++++++++++++ .../carbonado/info/StorableIntrospector.java | 48 ++++- 2 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/amazon/carbonado/info/ConversionComparator.java (limited to 'src/main/java/com/amazon/carbonado/info') diff --git a/src/main/java/com/amazon/carbonado/info/ConversionComparator.java b/src/main/java/com/amazon/carbonado/info/ConversionComparator.java new file mode 100644 index 0000000..dc88f95 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/info/ConversionComparator.java @@ -0,0 +1,212 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed 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 com.amazon.carbonado.info; + +import java.util.Comparator; + +import org.cojen.classfile.TypeDesc; + +/** + * Compares type conversions, finding the one that is nearest. + * + * @author Brian S O'Neill + */ +class ConversionComparator implements Comparator { + private final TypeDesc mFrom; + + public ConversionComparator(Class fromType) { + mFrom = TypeDesc.forClass(fromType); + } + + /** + * Returns true if a coversion is possible to the given type. + */ + public boolean isConversionPossible(Class toType) { + return isConversionPossible(mFrom, TypeDesc.forClass(toType)); + } + + @SuppressWarnings("unchecked") + private static boolean isConversionPossible(TypeDesc from, TypeDesc to) { + if (from == to) { + return true; + } + + if (from.toPrimitiveType() != null && to.toPrimitiveType() != null) { + from = from.toPrimitiveType(); + to = to.toPrimitiveType(); + } else { + from = from.toObjectType(); + to = to.toObjectType(); + } + + switch (from.getTypeCode()) { + case TypeDesc.OBJECT_CODE: default: + return to.toClass().isAssignableFrom(from.toClass()); + case TypeDesc.BOOLEAN_CODE: + return to == TypeDesc.BOOLEAN; + case TypeDesc.BYTE_CODE: + return to == TypeDesc.BYTE || to == TypeDesc.SHORT + || to == TypeDesc.INT || to == TypeDesc.LONG + || to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; + case TypeDesc.SHORT_CODE: + return to == TypeDesc.SHORT + || to == TypeDesc.INT || to == TypeDesc.LONG + || to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; + case TypeDesc.CHAR_CODE: + return to == TypeDesc.CHAR; + case TypeDesc.INT_CODE: + return to == TypeDesc.INT || to == TypeDesc.LONG || to == TypeDesc.DOUBLE; + case TypeDesc.FLOAT_CODE: + return to == TypeDesc.FLOAT || to == TypeDesc.DOUBLE; + case TypeDesc.LONG_CODE: + return to == TypeDesc.LONG; + case TypeDesc.DOUBLE_CODE: + return to == TypeDesc.DOUBLE; + } + } + + /** + * Evaluates two types, to see which one is nearest to the from type. + * Return <0 if "a" is nearest, 0 if both are equally good, >0 if "b" is + * nearest. + */ + public int compare(Class toType_a, Class toType_b) { + TypeDesc from = mFrom; + TypeDesc a = TypeDesc.forClass(toType_a); + TypeDesc b = TypeDesc.forClass(toType_b); + + if (from == a) { + if (from == b) { + return 0; + } + return -1; + } else if (from == b) { + return 1; + } + + int result = compare(from, a, b); + if (result != 0) { + return result; + } + + if (from.isPrimitive()) { + // Try boxing. + if (from.toObjectType() != null) { + from = from.toObjectType(); + return compare(from, a, b); + } + } else { + // Try unboxing. + if (from.toPrimitiveType() != null) { + from = from.toPrimitiveType(); + result = compare(from, a, b); + if (result != 0) { + return result; + } + // Try boxing back up. Test by unboxing 'to' types. + if (!toType_a.isPrimitive() && a.toPrimitiveType() != null) { + a = a.toPrimitiveType(); + } + if (!toType_b.isPrimitive() && b.toPrimitiveType() != null) { + b = b.toPrimitiveType(); + } + return compare(from, a, b); + } + } + + return 0; + } + + private static int compare(TypeDesc from, TypeDesc a, TypeDesc b) { + if (isConversionPossible(from, a)) { + if (isConversionPossible(from, b)) { + if (from.isPrimitive()) { + if (a.isPrimitive()) { + if (b.isPrimitive()) { + // Choose the one with the least amount of widening. + return primitiveWidth(a) - primitiveWidth(b); + } else { + return -1; + } + } else if (b.isPrimitive()) { + return 1; + } + } else { + // Choose the one with the shortest distance up the class + // hierarchy. + Class fromClass = from.toClass(); + if (!fromClass.isInterface()) { + if (a.toClass().isInterface()) { + if (!b.toClass().isInterface()) { + return -1; + } + } else if (b.toClass().isInterface()) { + return 1; + } else { + return distance(fromClass, a.toClass()) + - distance(fromClass, b.toClass()); + } + } + } + } else { + return -1; + } + } else if (isConversionPossible(from, b)) { + return 1; + } + + return 0; + } + + // 1 = boolean, 2 = byte, 3 = short, 4 = char, 5 = int, 6 = float, 7 = long, 8 = double + private static int primitiveWidth(TypeDesc type) { + switch (type.getTypeCode()) { + default: + return 0; + case TypeDesc.BOOLEAN_CODE: + return 1; + case TypeDesc.BYTE_CODE: + return 2; + case TypeDesc.SHORT_CODE: + return 3; + case TypeDesc.CHAR_CODE: + return 4; + case TypeDesc.INT_CODE: + return 5; + case TypeDesc.FLOAT_CODE: + return 6; + case TypeDesc.LONG_CODE: + return 7; + case TypeDesc.DOUBLE_CODE: + return 8; + } + } + + private static int distance(Class from, Class to) { + int distance = 0; + while (from != to) { + from = from.getSuperclass(); + if (from == null) { + return Integer.MAX_VALUE; + } + distance++; + } + return distance; + } +} diff --git a/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java b/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java index c527371..f318c33 100644 --- a/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java @@ -40,6 +40,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.cojen.classfile.MethodDesc; import org.cojen.classfile.TypeDesc; import org.cojen.util.BeanComparator; import org.cojen.util.BeanProperty; @@ -67,9 +68,6 @@ import com.amazon.carbonado.adapter.AdapterDefinition; import com.amazon.carbonado.constraint.ConstraintDefinition; import com.amazon.carbonado.lob.Lob; -import com.amazon.carbonado.spi.CodeBuilderUtil; -import com.amazon.carbonado.spi.ConversionComparator; - /** * Supports examination of {@link Storable} types, returning all metadata * associated with it. As part of the examination, all annotations are gathered @@ -470,7 +468,7 @@ public class StorableIntrospector { // Gather all methods. We'll be removing them as we implement them, // and if there are any abstract ones left over at the end, why, // that would be bad. - Map methods = CodeBuilderUtil.gatherAllDeclaredMethods(type); + Map methods = gatherAllDeclaredMethods(type); // Remove methods not abstract or defined explicitly in // Storable. Storable methods still must be implemented, but not as @@ -541,7 +539,7 @@ public class StorableIntrospector { } if (readMethod != null) { - String sig = CodeBuilderUtil.createSig(readMethod); + String sig = createSig(readMethod); if (methods.containsKey(sig)) { methods.remove(sig); properties.put(property.getName(), storableProp); @@ -551,7 +549,7 @@ public class StorableIntrospector { } if (writeMethod != null) { - String sig = CodeBuilderUtil.createSig(writeMethod); + String sig = createSig(writeMethod); if (methods.containsKey(sig)) { methods.remove(sig); properties.put(property.getName(), storableProp); @@ -1129,6 +1127,44 @@ public class StorableIntrospector { return (StorablePropertyAdapter[]) list.toArray(new StorablePropertyAdapter[list.size()]); } + /** + * Returns a new modifiable mapping of method signatures to methods. + * + * @return map of {@link #createSig signatures} to methods + */ + private static Map gatherAllDeclaredMethods(Class clazz) { + Map methods = new HashMap(); + gatherAllDeclaredMethods(methods, clazz); + return methods; + } + + private static void gatherAllDeclaredMethods(Map methods, Class clazz) { + for (Method m : clazz.getDeclaredMethods()) { + String desc = createSig(m); + if (!methods.containsKey(desc)) { + methods.put(desc, m); + } + } + + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + gatherAllDeclaredMethods(methods, superclass); + } + for (Class c : clazz.getInterfaces()) { + gatherAllDeclaredMethods(methods, c); + } + } + + /** + * Create a representation of the signature which includes the method name. + * This uniquely identifies the method. + * + * @param m method to describe + */ + private static String createSig(Method m) { + return m.getName() + ':' + MethodDesc.forMethod(m).getDescriptor(); + } + private static final class Info implements StorableInfo { private final Class mType; private final String[] mAliases; -- cgit v1.2.3