From fec4f593ccac17493f0014bd8fabdedc278135c8 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Tue, 22 Jul 2008 09:19:51 +0000 Subject: Added advanced conversion capability when setting query filter properties. This is used to ensure that BigDecimal values are properly normalized. --- .../com/amazon/carbonado/filter/FilterValues.java | 29 +- .../amazon/carbonado/filter/PropertyFilter.java | 214 +++----- .../com/amazon/carbonado/gen/MasterFeature.java | 3 + .../carbonado/gen/MasterStorableGenerator.java | 137 ++++- .../amazon/carbonado/gen/StorableGenerator.java | 4 +- .../carbonado/info/ConversionComparator.java | 213 -------- .../carbonado/info/StorableIntrospector.java | 1 + .../carbonado/raw/GenericEncodingStrategy.java | 128 ++--- .../amazon/carbonado/raw/GenericStorableCodec.java | 55 +- .../amazon/carbonado/raw/RawStorableGenerator.java | 4 +- .../com/amazon/carbonado/repo/map/MapStorage.java | 4 +- .../carbonado/util/ConversionComparator.java | 213 ++++++++ .../java/com/amazon/carbonado/util/Converter.java | 602 +++++++++++++++++++++ 13 files changed, 1109 insertions(+), 498 deletions(-) delete mode 100644 src/main/java/com/amazon/carbonado/info/ConversionComparator.java create mode 100644 src/main/java/com/amazon/carbonado/util/ConversionComparator.java create mode 100644 src/main/java/com/amazon/carbonado/util/Converter.java (limited to 'src/main') diff --git a/src/main/java/com/amazon/carbonado/filter/FilterValues.java b/src/main/java/com/amazon/carbonado/filter/FilterValues.java index 6e7202c..a56fc22 100644 --- a/src/main/java/com/amazon/carbonado/filter/FilterValues.java +++ b/src/main/java/com/amazon/carbonado/filter/FilterValues.java @@ -99,7 +99,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(int.class, value); + throw mismatch(e); } return with(current, obj); } @@ -117,7 +117,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(long.class, value); + throw mismatch(e); } return with(current, obj); } @@ -135,7 +135,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(float.class, value); + throw mismatch(e); } return with(current, obj); } @@ -153,7 +153,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(double.class, value); + throw mismatch(e); } return with(current, obj); } @@ -171,7 +171,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(boolean.class, value); + throw mismatch(e); } return with(current, obj); } @@ -189,7 +189,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(char.class, value); + throw mismatch(e); } return with(current, obj); } @@ -207,7 +207,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(byte.class, value); + throw mismatch(e); } return with(current, obj); } @@ -225,7 +225,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(short.class, value); + throw mismatch(e); } return with(current, obj); } @@ -243,7 +243,7 @@ public class FilterValues implements Serializable, Appender try { obj = current.getPropertyFilter().adaptValue(value); } catch (IllegalArgumentException e) { - throw mismatch(value == null ? null : value.getClass(), value); + throw mismatch(e); } return with(current, obj); } @@ -672,17 +672,12 @@ public class FilterValues implements Serializable, Appender return current; } - private IllegalArgumentException mismatch(Class actualType, Object actualValue) { + private IllegalArgumentException mismatch(IllegalArgumentException e) { PropertyFilterList current = currentProperty(); PropertyFilter propFilter = current.getPropertyFilter(); StringBuilder b = new StringBuilder(); - - try { - propFilter.appendMismatchMessage(b, actualType, actualValue); - } catch (IOException e) { - // Not gonna happen - } + b.append(e.getMessage()); int subFilterCount = current.getPreviousRemaining() + current.getNextRemaining() + 1; @@ -696,7 +691,7 @@ public class FilterValues implements Serializable, Appender b.append(" sub filter in \""); try { appendTo(b); - } catch (IOException e) { + } catch (IOException e2) { // Not gonna happen } b.append('"'); diff --git a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java index 5014733..2cecca3 100644 --- a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java +++ b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java @@ -19,6 +19,10 @@ package com.amazon.carbonado.filter; import java.io.IOException; + +import java.math.BigDecimal; +import java.math.BigInteger; + import java.util.Collections; import java.util.List; @@ -28,6 +32,8 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.info.ChainedProperty; import com.amazon.carbonado.info.StorableProperty; +import com.amazon.carbonado.util.Converter; + /** * Filter tree node that performs a relational test against a specific property * value. @@ -38,7 +44,13 @@ public class PropertyFilter extends Filter { private static final long serialVersionUID = 1L; // Indicates property has been bound to a constant value. - private static int BOUND_CONSTANT = -1; + private static final int BOUND_CONSTANT = -1; + + private static final Converter cConverter; + + static { + cConverter = Converter.build(Hidden.Adapter.class); + } /** * Returns a canonical instance, creating a new one if there isn't one @@ -334,171 +346,63 @@ public class PropertyFilter extends Filter { * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(int value) { - Class type = getBoxedType(); - if (type == Integer.class) { - return Integer.valueOf(value); - } else if (type == Long.class) { - return Long.valueOf(value); - } else if (type == Double.class) { - return Double.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Integer.valueOf(value); - } - throw mismatch(int.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(long value) { - Class type = getBoxedType(); - if (type == Long.class) { - return Long.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Long.valueOf(value); - } - throw mismatch(long.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(float value) { - Class type = getBoxedType(); - if (type == Float.class) { - return Float.valueOf(value); - } else if (type == Double.class) { - return Double.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Float.valueOf(value); - } - throw mismatch(float.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(double value) { - Class type = getBoxedType(); - if (type == Double.class) { - return Double.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Double.valueOf(value); - } - throw mismatch(float.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(boolean value) { - Class type = getBoxedType(); - if (type == Boolean.class || type == Object.class) { - return Boolean.valueOf(value); - } - throw mismatch(boolean.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(char value) { - Class type = getBoxedType(); - if (type == Character.class || type == Object.class) { - return Character.valueOf(value); - } else if (type == String.class || type == CharSequence.class) { - return String.valueOf(value); - } - throw mismatch(char.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(byte value) { - Class type = getBoxedType(); - if (type == Byte.class) { - return Byte.valueOf(value); - } else if (type == Short.class) { - return Short.valueOf(value); - } else if (type == Integer.class) { - return Integer.valueOf(value); - } else if (type == Long.class) { - return Long.valueOf(value); - } else if (type == Double.class) { - return Double.valueOf(value); - } else if (type == Float.class) { - return Float.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Byte.valueOf(value); - } - throw mismatch(byte.class, value); + return cConverter.convert(value, getType()); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(short value) { - Class type = getBoxedType(); - if (type == Short.class) { - return Short.valueOf(value); - } else if (type == Integer.class) { - return Integer.valueOf(value); - } else if (type == Long.class) { - return Long.valueOf(value); - } else if (type == Double.class) { - return Double.valueOf(value); - } else if (type == Float.class) { - return Float.valueOf(value); - } else if (type == Number.class || type == Object.class) { - return Short.valueOf(value); - } - throw mismatch(short.class, value); + return cConverter.convert(value, Object.class); } /** * @throws IllegalArgumentException if type doesn't match */ Object adaptValue(Object value) { - if (getBoxedType().isInstance(value)) { - return value; - } - - Class type = getType(); - - if (value == null) { - if (!type.isPrimitive()) { - return value; - } - } else if (type.isPrimitive()) { - TypeDesc actualPrim = TypeDesc.forClass(value.getClass()).toPrimitiveType(); - if (actualPrim != null) { - if (type == actualPrim.toClass()) { - return value; - } - // Unbox and rebox. - switch (actualPrim.getTypeCode()) { - case TypeDesc.BYTE_CODE: - return adaptValue(((Number) value).byteValue()); - case TypeDesc.SHORT_CODE: - return adaptValue(((Number) value).shortValue()); - case TypeDesc.INT_CODE: - return adaptValue(((Number) value).intValue()); - case TypeDesc.LONG_CODE: - return adaptValue(((Number) value).longValue()); - case TypeDesc.FLOAT_CODE: - return adaptValue(((Number) value).floatValue()); - case TypeDesc.DOUBLE_CODE: - return adaptValue(((Number) value).doubleValue()); - case TypeDesc.BOOLEAN_CODE: - return adaptValue(((Boolean) value).booleanValue()); - case TypeDesc.CHAR_CODE: - return adaptValue(((Character) value).charValue()); - } - } - } - - throw mismatch(value == null ? null : value.getClass(), value); + return cConverter.convert(value, getType()); } @Override @@ -546,29 +450,59 @@ public class PropertyFilter extends Filter { } } - void appendMismatchMessage(Appendable a, Class actualType, Object actualValue) - throws IOException - { - if (actualType == null || actualValue == null) { - a.append("Actual value is null, which cannot be assigned to type \""); - } else { - a.append("Actual value \""); - a.append(String.valueOf(actualValue)); - a.append("\", of type \""); - a.append(TypeDesc.forClass(actualType).getFullName()); - a.append("\", is incompatible with expected type of \""); - } - a.append(TypeDesc.forClass(getType()).getFullName()); - a.append('"'); - } + private static class Hidden { + public static abstract class Adapter extends Converter { + public String convertToString(char value) { + return String.valueOf(value); + } + + public CharSequence convertToCharSequence(char value) { + return String.valueOf(value); + } + + public String convertToString(StringBuffer value) { + return value.toString(); + } + + public String convertToString(StringBuilder value) { + return value.toString(); + } + + public BigInteger convertToBigInteger(long value) { + return BigInteger.valueOf(value); + } - private IllegalArgumentException mismatch(Class actualType, Object actualValue) { - StringBuilder b = new StringBuilder(); - try { - appendMismatchMessage(b, actualType, actualValue); - } catch (IOException e) { - // Not gonna happen + public BigDecimal convertToBigDecimal(long value) { + if (value > -10 && value < 10) { + return BigDecimal.valueOf(value); + } + // Normalize value. + return BigDecimal.valueOf(value).stripTrailingZeros(); + } + + public BigDecimal convertToBigDecimal(double value) { + if (value == 0) { + return BigDecimal.ZERO; + } + // Normalize value. + return BigDecimal.valueOf(value).stripTrailingZeros(); + } + + public BigDecimal convertToBigDecimal(BigInteger value) { + if (BigInteger.ZERO.equals(value)) { + return BigDecimal.ZERO; + } + // Normalize value. + return new BigDecimal(value, 0).stripTrailingZeros(); + } + + public BigDecimal convertToBigDecimal(BigDecimal value) { + if (value.compareTo(BigDecimal.ZERO) == 0) { + return BigDecimal.ZERO; + } + // Normalize value. + return value.stripTrailingZeros(); + } } - return new IllegalArgumentException(b.toString()); } } diff --git a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java index a1bb278..f433ce6 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterFeature.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterFeature.java @@ -28,6 +28,9 @@ public enum MasterFeature { /** Insert and update operations implement record versioning, if version property exists */ VERSIONING, + /** Insert and update operations normalize property types such as BigDecimal */ + NORMALIZE, + /** Update operations load clean copy first, to prevent destructive update */ UPDATE_FULL, diff --git a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java index d8d2901..092eea8 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java @@ -19,8 +19,13 @@ package com.amazon.carbonado.gen; import java.lang.reflect.Method; + +import java.math.BigDecimal; + +import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; +import java.util.List; import java.util.Map; import org.cojen.classfile.ClassFile; @@ -489,9 +494,19 @@ public final class MasterStorableGenerator { isInitialized.setLocation(); } + // Copy of properties before normalization. + List unnormalized = null; + if (mFeatures.contains(MasterFeature.NORMALIZE)) { + unnormalized = addNormalization(b, false); + } + + Label doTryStart = b.createLabel().setLocation(); + b.loadThis(); b.invokeVirtual(DO_TRY_INSERT_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); + addNormalizationRollback(b, doTryStart, unnormalized); + if (tryStart == null) { b.returnValue(TypeDesc.BOOLEAN); } else { @@ -520,6 +535,7 @@ public final class MasterStorableGenerator { CodeBuilder b = new CodeBuilder(mi); if ((!mFeatures.contains(MasterFeature.VERSIONING)) && + (!mFeatures.contains(MasterFeature.NORMALIZE)) && (!mFeatures.contains(MasterFeature.UPDATE_FULL)) && (!mFeatures.contains(MasterFeature.UPDATE_TXN))) { @@ -539,7 +555,22 @@ public final class MasterStorableGenerator { Label tryLoadStart = null, tryLoadEnd = null; - if (mFeatures.contains(MasterFeature.UPDATE_FULL)) { + // Copy of properties before normalization. + List unnormalized = null; + if (mFeatures.contains(MasterFeature.NORMALIZE)) { + unnormalized = addNormalization(b, false); + } + + Label doTryStart = b.createLabel().setLocation(); + + if (!mFeatures.contains(MasterFeature.UPDATE_FULL)) { + // if (!this.doTryUpdateMaster()) { + // goto failed; + // } + b.loadThis(); + b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); + b.ifZeroComparisonBranch(failed, "=="); + } else { // Storable saved = copy(); b.loadThis(); b.invokeVirtual(COPY_METHOD_NAME, storableType, null); @@ -701,13 +732,6 @@ public final class MasterStorableGenerator { b.loadThis(); b.invokeInterface (storableType, COPY_UNEQUAL_PROPERTIES, null, new TypeDesc[] {storableType}); - } else { - // if (!this.doTryUpdateMaster()) { - // goto failed; - // } - b.loadThis(); - b.invokeVirtual(DO_TRY_UPDATE_MASTER_METHOD_NAME, TypeDesc.BOOLEAN, null); - b.ifZeroComparisonBranch(failed, "=="); } // txn.commit(); @@ -1023,4 +1047,101 @@ public final class MasterStorableGenerator { b.invoke(versionProperty.getWriteMethod()); } + + private List addNormalization(CodeBuilder b, boolean forUpdate) { + List unnormalized = null; + + for (StorableProperty property : mAllProperties.values()) { + if (property.isDerived()) { + continue; + } + if (!BigDecimal.class.isAssignableFrom(property.getType())) { + continue; + } + + if (unnormalized == null) { + unnormalized = new ArrayList(); + } + + PropertyCopy copy = new PropertyCopy(b, property); + unnormalized.add(copy); + + copy.makeCopy(b); + + b.loadLocal(copy.copyVar); + Label skipNormalize = b.createLabel(); + b.ifNullBranch(skipNormalize, true); + + if (forUpdate) { + // FIXME: for update, also check if dirty + } + + // Normalize by stripping trailing zeros. + // FIXME: Workaround BigDecimal.ZERO bug. + b.loadThis(); + b.loadLocal(copy.copyVar); + TypeDesc propertyType = copy.copyVar.getType(); + b.invokeVirtual(propertyType, "stripTrailingZeros", propertyType, null); + b.storeField(property.getName(), propertyType); + + skipNormalize.setLocation(); + } + + return unnormalized; + } + + /** + * Assumes a boolean is on the stack, as returned by doTryInsert or doTryUpdate. + */ + private void addNormalizationRollback(CodeBuilder b, Label doTryStart, + List unnormalized) + { + if (unnormalized != null) { + Label doTryEnd = b.createLabel().setLocation(); + + b.dup(); + Label success = b.createLabel(); + b.ifZeroComparisonBranch(success, "!="); + + for (int i=0; i<2; i++) { + if (i == 0) { + } else { + b.exceptionHandler(doTryStart, doTryEnd, null); + } + // Rollback normalized properties. + for (PropertyCopy copy : unnormalized) { + copy.restore(b); + } + if (i == 0) { + b.branch(success); + } else { + b.throwObject(); + } + } + + success.setLocation(); + } + } + + private static class PropertyCopy { + final StorableProperty property; + final LocalVariable copyVar; + + PropertyCopy(CodeBuilder b, StorableProperty property) { + this.property = property; + copyVar = b.createLocalVariable(null, TypeDesc.forClass(property.getType())); + } + + void makeCopy(CodeBuilder b) { + b.loadThis(); + b.loadField(property.getName(), copyVar.getType()); + b.storeLocal(copyVar); + } + + void restore(CodeBuilder b) { + b.loadThis(); + b.loadLocal(copyVar); + b.storeField(property.getName(), copyVar.getType()); + } + } } diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index 0eee3de..e88ba39 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -2732,7 +2732,7 @@ public final class StorableGenerator { LocalVariable encodedVar; try { - encodedVar = encoder.buildSerialEncoding(b, null, null); + encodedVar = encoder.buildSerialEncoding(b, null); } catch (SupportException e) { CodeBuilderUtil.throwException(b, SupportException.class, e.getMessage()); return; @@ -2780,7 +2780,7 @@ public final class StorableGenerator { GenericEncodingStrategy encoder = new GenericEncodingStrategy(mStorableType, null); try { - encoder.buildSerialDecoding(b, null, null, encodedVar); + encoder.buildSerialDecoding(b, null, encodedVar); } catch (SupportException e) { CodeBuilderUtil.throwException(b, SupportException.class, e.getMessage()); return; diff --git a/src/main/java/com/amazon/carbonado/info/ConversionComparator.java b/src/main/java/com/amazon/carbonado/info/ConversionComparator.java deleted file mode 100644 index 0feac47..0000000 --- a/src/main/java/com/amazon/carbonado/info/ConversionComparator.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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 - * @since 1.2 - */ -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 {@literal <0} if "a" is nearest, 0 if both are equally good, - * {@literal >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 80d263a..bd1d52b 100644 --- a/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/info/StorableIntrospector.java @@ -72,6 +72,7 @@ import com.amazon.carbonado.Version; import com.amazon.carbonado.adapter.AdapterDefinition; import com.amazon.carbonado.constraint.ConstraintDefinition; import com.amazon.carbonado.lob.Lob; +import com.amazon.carbonado.util.ConversionComparator; /** * Supports examination of {@link Storable} types, returning all metadata diff --git a/src/main/java/com/amazon/carbonado/raw/GenericEncodingStrategy.java b/src/main/java/com/amazon/carbonado/raw/GenericEncodingStrategy.java index e7a8ff9..c5417c4 100644 --- a/src/main/java/com/amazon/carbonado/raw/GenericEncodingStrategy.java +++ b/src/main/java/com/amazon/carbonado/raw/GenericEncodingStrategy.java @@ -25,7 +25,6 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; -import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -69,26 +68,6 @@ import com.amazon.carbonado.info.StorablePropertyAdapter; * @author Brian S O'Neill */ public class GenericEncodingStrategy { - /** - * Defines extra encoding options. - * - * @since 1.2 - */ - public static enum Option { - /** - * Access properties by public methods instead of protected fields. - * Option should be used if class being generated doesn't have access - * to these fields. - */ - USE_METHODS, - - /** - * Property values such as BigDecimal are normalized before being - * encoded. - */ - NORMALIZE, - } - private static enum Mode { KEY, DATA, SERIAL } private final Class mType; @@ -165,7 +144,9 @@ public class GenericEncodingStrategy { * of a Storable instance. * @param adapterInstanceClass class containing static references to * adapter instances - defaults to instanceVar - * @param options optional encoding options + * @param useReadMethods when true, access properties by public read + * methods instead of protected fields - should be used if class being + * generated doesn't have access to these fields * @param partialStartVar optional variable for supporting partial key * generation. It must be an int, whose runtime value must be less than the * properties array length. It marks the range start of the partial @@ -186,7 +167,7 @@ public class GenericEncodingStrategy { OrderedProperty[] properties, LocalVariable instanceVar, Class adapterInstanceClass, - EnumSet