From 51fb368d2899e42f667291c75a4e68291dbae205 Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Tue, 28 Aug 2007 05:12:49 +0000
Subject: Storable toString and toStringKeyOnly methods skip uninitialized
 properties.

---
 RELEASE-NOTES.txt                                  |   3 +-
 src/main/java/com/amazon/carbonado/Storable.java   |   8 +-
 .../amazon/carbonado/gen/StorableGenerator.java    | 116 +++++++++++----------
 3 files changed, 66 insertions(+), 61 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 01bc201..e50fd25 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -19,7 +19,7 @@ Carbonado change history
 - JDBC repository supports optional automatic version management, eliminating
   the requirement that triggers be installed on the database.
 - FilteredCursor ensures that filter being used is bound.
-- BDBRepository detects if changes are made to primary key and throws exception.
+- BDBRepository detects if changes are made to primary key definition and throws exception.
 - Added support for derived properies.
 - Enhanced query engine to optimize for covering indexes.
 - Added methods to access Storable properties by name.
@@ -31,6 +31,7 @@ Carbonado change history
 - Added support for defining alternate keys in synthetic Storables.
 - Added trigger support for after loads and queries.
 - Removed vestigial support for wrapping Storables.
+- Storable toString and toStringKeyOnly methods skip uninitialized properties.
 
 1.1 to 1.1.2
 -------------------------------
diff --git a/src/main/java/com/amazon/carbonado/Storable.java b/src/main/java/com/amazon/carbonado/Storable.java
index e745040..9f83dee 100644
--- a/src/main/java/com/amazon/carbonado/Storable.java
+++ b/src/main/java/com/amazon/carbonado/Storable.java
@@ -448,14 +448,14 @@ public interface Storable<S extends Storable<S>> {
 
     /**
      * Returns a string for debugging purposes that contains all supported
-     * property names and values for this object. Unsupported {@link
-     * Independent independent} properties are not included.
+     * property names and values for this object. Uninitialized and unsupported
+     * {@link Independent independent} properties are not included.
      */
     String toString();
 
     /**
-     * Returns a string for debugging purposes that contains supported
-     * key property names and values for this object. Unsupported
+     * Returns a string for debugging purposes that contains supported key
+     * property names and values for this object. Uninitialized and unsupported
      * {@link Independent independent} properties are not included.
      */
     String toStringKeyOnly();
diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
index b4ed7c2..4bc5010 100644
--- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
+++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java
@@ -24,6 +24,7 @@ import java.lang.ref.SoftReference;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -2054,6 +2055,7 @@ public final class StorableGenerator<S extends Storable> {
     /**
      * Generates code to set all state properties to zero.
      */
+    /*
     private void clearState(CodeBuilder b) {
         int ordinal = -1;
         int maxOrdinal = mAllProperties.size() - 1;
@@ -2078,6 +2080,7 @@ public final class StorableGenerator<S extends Storable> {
             }
         }
     }
+    */
 
     private void addMarkCleanMethod(String name) {
         MethodInfo mi =
@@ -2966,8 +2969,6 @@ public final class StorableGenerator<S extends Storable> {
      */
     private void addToStringMethod(boolean keyOnly) {
         TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class);
-        TypeDesc[] stringParam = {TypeDesc.STRING};
-        TypeDesc[] charParam = {TypeDesc.CHAR};
 
         Modifiers modifiers = Modifiers.PUBLIC.toSynchronized(true);
         MethodInfo mi = addMethodIfNotFinal(modifiers,
@@ -2985,7 +2986,7 @@ public final class StorableGenerator<S extends Storable> {
         b.dup();
         b.invokeConstructor(stringBuilder, null);
         b.loadConstant(mStorableType.getName());
-        invokeAppend(b, stringParam);
+        invokeAppend(b, TypeDesc.STRING);
 
         String detail;
         if (keyOnly) {
@@ -2995,87 +2996,62 @@ public final class StorableGenerator<S extends Storable> {
         }
 
         b.loadConstant(detail);
-        invokeAppend(b, stringParam);
+        invokeAppend(b, TypeDesc.STRING);
 
-        // First pass, just print primary keys.
+        // First pass, just print primary keys. Also gather ordinals.
+        
+        Map<Object, Integer> ordinals = new IdentityHashMap<Object, Integer>();
         int ordinal = 0;
-        for (StorableProperty property : mAllProperties.values()) {
-            if (property.isPrimaryKeyMember()) {
-                Label skipPrint = b.createLabel();
-
-                // Check if independent property is supported, and skip if not.
-                if (property.isIndependent()) {
-                    addSkipIndependent(b, null, property, skipPrint);
-                }
 
-                if (ordinal++ > 0) {
-                    b.loadConstant(", ");
-                    invokeAppend(b, stringParam);
-                }
-                addPropertyAppendCall(b, property, stringParam, charParam);
+        LocalVariable commaCountVar = b.createLocalVariable(null, TypeDesc.INT);
+        b.loadConstant(-1);
+        b.storeLocal(commaCountVar);
 
-                skipPrint.setLocation();
+        for (StorableProperty property : mAllProperties.values()) {
+            ordinals.put(property, ordinal);
+            if (property.isPrimaryKeyMember()) {
+                addPropertyAppendCall(b, property, commaCountVar, ordinal);
             }
+            ordinal++;
         }
 
         // Second pass, print non-primary keys.
+
         if (!keyOnly) {
             for (StorableProperty property : mAllProperties.values()) {
                 // Don't print any derived or join properties since they may throw an exception.
                 if (!property.isPrimaryKeyMember() &&
                     (!property.isDerived()) && (!property.isJoin()))
                 {
-                    Label skipPrint = b.createLabel();
-
-                    // Check if independent property is supported, and skip if not.
-                    if (property.isIndependent()) {
-                        addSkipIndependent(b, null, property, skipPrint);
-                    }
-
-                    b.loadConstant(", ");
-                    invokeAppend(b, stringParam);
-                    addPropertyAppendCall(b, property, stringParam, charParam);
-
-                    skipPrint.setLocation();
+                    addPropertyAppendCall(b, property, commaCountVar, ordinals.get(property));
                 }
             }
         }
 
         b.loadConstant('}');
-        invokeAppend(b, charParam);
+        invokeAppend(b, TypeDesc.CHAR);
 
         // For key string, also show all the alternate keys. This makes the
         // FetchNoneException message more helpful.
         if (keyOnly) {
             int altKeyCount = mInfo.getAlternateKeyCount();
             for (int i=0; i<altKeyCount; i++) {
+                b.loadConstant(-1);
+                b.storeLocal(commaCountVar);
+
                 b.loadConstant(", {");
-                invokeAppend(b, stringParam);
+                invokeAppend(b, TypeDesc.STRING);
 
                 StorableKey<S> key = mInfo.getAlternateKey(i);
 
                 ordinal = 0;
                 for (OrderedProperty<S> op : key.getProperties()) {
                     StorableProperty<S> property = op.getChainedProperty().getPrimeProperty();
-
-                    Label skipPrint = b.createLabel();
-
-                    // Check if independent property is supported, and skip if not.
-                    if (property.isIndependent()) {
-                        addSkipIndependent(b, null, property, skipPrint);
-                    }
-
-                    if (ordinal++ > 0) {
-                        b.loadConstant(", ");
-                        invokeAppend(b, stringParam);
-                    }
-                    addPropertyAppendCall(b, property, stringParam, charParam);
-
-                    skipPrint.setLocation();
+                    addPropertyAppendCall(b, property, commaCountVar, ordinals.get(property));
                 }
 
                 b.loadConstant('}');
-                invokeAppend(b, charParam);
+                invokeAppend(b, TypeDesc.CHAR);
             }
         }
 
@@ -3083,20 +3059,48 @@ public final class StorableGenerator<S extends Storable> {
         b.returnValue(TypeDesc.STRING);
     }
 
-    private void invokeAppend(CodeBuilder b, TypeDesc[] paramType) {
+    private void invokeAppend(CodeBuilder b, TypeDesc type) {
         TypeDesc stringBuilder = TypeDesc.forClass(StringBuilder.class);
-        b.invokeVirtual(stringBuilder, "append", stringBuilder, paramType);
+        b.invokeVirtual(stringBuilder, "append", stringBuilder, new TypeDesc[] {type});
     }
 
     private void addPropertyAppendCall(CodeBuilder b,
                                        StorableProperty property,
-                                       TypeDesc[] stringParam,
-                                       TypeDesc[] charParam)
+                                       LocalVariable commaCountVar,
+                                       int ordinal)
     {
+        Label skipPrint = b.createLabel();
+
+        // Check if independent property is supported, and skip if not.
+        if (property.isIndependent()) {
+            addSkipIndependent(b, null, property, skipPrint);
+        }
+
+        // Check if property is initialized, and skip if not.
+        b.loadThis();
+        b.loadField(PROPERTY_STATE_FIELD_NAME + (ordinal >> 4), TypeDesc.INT);
+        b.loadConstant(PROPERTY_STATE_MASK << ((ordinal & 0xf) * 2));
+        b.math(Opcode.IAND);
+        b.ifZeroComparisonBranch(skipPrint, "==");
+
+        b.integerIncrement(commaCountVar, 1);
+        b.loadLocal(commaCountVar);
+        Label noComma = b.createLabel();
+        b.ifZeroComparisonBranch(noComma, "==");
+        b.loadConstant(", ");
+        invokeAppend(b, TypeDesc.STRING);
+        noComma.setLocation();
+
+        addPropertyAppendCall(b, property);
+
+        skipPrint.setLocation();
+    }
+
+    private void addPropertyAppendCall(CodeBuilder b, StorableProperty property) {
         b.loadConstant(property.getName());
-        invokeAppend(b, stringParam);
+        invokeAppend(b, TypeDesc.STRING);
         b.loadConstant('=');
-        invokeAppend(b, charParam);
+        invokeAppend(b, TypeDesc.CHAR);
         b.loadThis();
         TypeDesc type = TypeDesc.forClass(property.getType());
         b.loadField(property.getName(), type);
@@ -3119,7 +3123,7 @@ public final class StorableGenerator<S extends Storable> {
                 type = TypeDesc.OBJECT;
             }
         }
-        invokeAppend(b, new TypeDesc[]{type});
+        invokeAppend(b, type);
     }
 
     /**
-- 
cgit v1.2.3