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/gen/MasterFeature.java | 3 + .../carbonado/gen/MasterStorableGenerator.java | 137 +++++++++++++++++++-- .../amazon/carbonado/gen/StorableGenerator.java | 4 +- 3 files changed, 134 insertions(+), 10 deletions(-) (limited to 'src/main/java/com/amazon/carbonado/gen') 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; -- cgit v1.2.3