diff options
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
| -rw-r--r-- | src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java | 106 | 
1 files changed, 104 insertions, 2 deletions
| diff --git a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java index 1d9b7c5..11657de 100644 --- a/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/spi/MasterStorableGenerator.java @@ -20,6 +20,7 @@ package com.amazon.carbonado.spi;  import java.lang.reflect.Method;
  import java.util.EnumSet;
 +import java.util.HashSet;
  import java.util.Map;
  import org.cojen.classfile.ClassFile;
 @@ -70,6 +71,8 @@ public final class MasterStorableGenerator<S extends Storable> {          DO_TRY_UPDATE_MASTER_METHOD_NAME = "doTryUpdate$master",
          DO_TRY_DELETE_MASTER_METHOD_NAME = "doTryDelete$master";
 +    private static final String APPEND_UNINIT_PROPERTY = "appendUninitializedPropertyName$";
 +
      private static final String INSERT_OP = "Insert";
      private static final String UPDATE_OP = "Update";
      private static final String DELETE_OP = "Delete";
 @@ -374,8 +377,107 @@ public final class MasterStorableGenerator<S extends Storable> {                                  TypeDesc.BOOLEAN, null);
                  Label isInitialized = b.createLabel();
                  b.ifZeroComparisonBranch(isInitialized, "!=");
 -                CodeBuilderUtil.throwException(b, ConstraintException.class,
 -                                               "Not all required properties have been set");
 +
 +                // Throw a ConstraintException.
 +                TypeDesc exType = TypeDesc.forClass(ConstraintException.class);
 +                b.newObject(exType);
 +                b.dup();
 +
 +                // Append all the uninitialized property names to the exception message.
 +
 +                LocalVariable countVar = b.createLocalVariable(null, TypeDesc.INT);
 +                b.loadConstant(0);
 +                b.storeLocal(countVar);
 +
 +                TypeDesc sbType = TypeDesc.forClass(StringBuilder.class);
 +                b.newObject(sbType);
 +                b.dup();
 +                b.loadConstant("Not all required properties have been set: ");
 +                TypeDesc[] stringParam = {TypeDesc.STRING};
 +                b.invokeConstructor(sbType, stringParam);
 +                LocalVariable sbVar = b.createLocalVariable(null, sbType);
 +                b.storeLocal(sbVar);
 +
 +                int ordinal = -1;
 +
 +                HashSet<Integer> stateAppendMethods = new HashSet<Integer>();
 +
 +                // Parameters are: StringBuilder, count, mask, property name
 +                TypeDesc[] appendParams = {sbType, TypeDesc.INT, TypeDesc.INT, TypeDesc.STRING};
 +
 +                for (StorableProperty<S> property : mAllProperties.values()) {
 +                    ordinal++;
 +
 +                    if (property.isJoin() || property.isPrimaryKeyMember()
 +                        || property.isNullable())
 +                    {
 +                        continue;
 +                    }
 +
 +                    int stateField = ordinal >> 4;
 +
 +                    String stateAppendMethodName = APPEND_UNINIT_PROPERTY + stateField;
 +
 +                    if (!stateAppendMethods.contains(stateField)) {
 +                        stateAppendMethods.add(stateField);
 +
 +                        MethodInfo mi2 = mClassFile.addMethod
 +                            (Modifiers.PRIVATE, stateAppendMethodName, TypeDesc.INT, appendParams);
 +
 +                        CodeBuilder b2 = new CodeBuilder(mi2);
 +
 +                        // Load the StringBuilder parameter.
 +                        b2.loadLocal(b2.getParameter(0));
 +
 +                        String stateFieldName =
 +                            StorableGenerator.PROPERTY_STATE_FIELD_NAME + (ordinal >> 4);
 +
 +                        b2.loadThis();
 +                        b2.loadField(stateFieldName, TypeDesc.INT);
 +                        // Load the mask parameter.
 +                        b2.loadLocal(b2.getParameter(2));
 +                        b2.math(Opcode.IAND);
 +                        
 +                        Label propIsInitialized = b2.createLabel();
 +                        b2.ifZeroComparisonBranch(propIsInitialized, "!=");
 +
 +                        // Load the count parameter.
 +                        b2.loadLocal(b2.getParameter(1));
 +                        Label noComma = b2.createLabel();
 +                        b2.ifZeroComparisonBranch(noComma, "==");
 +                        b2.loadConstant(", ");
 +                        b2.invokeVirtual(sbType, "append", sbType, stringParam);
 +                        noComma.setLocation();
 +
 +                        // Load the property name parameter.
 +                        b2.loadLocal(b2.getParameter(3));
 +                        b2.invokeVirtual(sbType, "append", sbType, stringParam);
 +
 +                        // Increment the count parameter.
 +                        b2.integerIncrement(b2.getParameter(1), 1);
 +
 +                        propIsInitialized.setLocation();
 +
 +                        // Return the possibly updated count.
 +                        b2.loadLocal(b2.getParameter(1));
 +                        b2.returnValue(TypeDesc.INT);
 +                    }
 +
 +                    b.loadThis();
 +                    // Parameters are: StringBuilder, count, mask, property name
 +                    b.loadLocal(sbVar);
 +                    b.loadLocal(countVar);
 +                    b.loadConstant(StorableGenerator.PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2));
 +                    b.loadConstant(property.getName());
 +                    b.invokePrivate(stateAppendMethodName, TypeDesc.INT, appendParams);
 +                    b.storeLocal(countVar);
 +                }
 +
 +                b.loadLocal(sbVar);
 +                b.invokeVirtual(sbType, "toString", TypeDesc.STRING, null);
 +                b.invokeConstructor(exType, new TypeDesc[] {TypeDesc.STRING});
 +                b.throwObject();
 +
                  isInitialized.setLocation();
              }
 | 
