jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r1134274 - in /jackrabbit/sandbox/microkernel/src: main/java/org/apache/jackrabbit/mk/ main/java/org/apache/jackrabbit/mk/json/ main/java/org/apache/jackrabbit/mk/mem/ test/java/org/apache/jackrabbit/mk/ test/java/org/apache/jackrabbit/mk/j...
Date Fri, 10 Jun 2011 10:50:09 GMT
Author: thomasm
Date: Fri Jun 10 10:50:08 2011
New Revision: 1134274

URL: http://svn.apache.org/viewvc?rev=1134274&view=rev
Log:
Improved Json parsing/building, begin work on data type support.

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java
      - copied, changed from r1134216, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java
      - copied, changed from r1134215, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopTokenizer.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/ValTest.java
Removed:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopTokenizer.java
Modified:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Val.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
Fri Jun 10 10:50:08 2011
@@ -21,9 +21,8 @@ import org.apache.jackrabbit.mk.api.Micr
 import org.apache.jackrabbit.mk.json.DiffHandler;
 import org.apache.jackrabbit.mk.json.DiffParser;
 import org.apache.jackrabbit.mk.json.JsonBuilder;
+import org.apache.jackrabbit.mk.json.JsopBuilder;
 import org.apache.jackrabbit.mk.json.JsonBuilder.JsonObjectBuilder;
-import org.apache.jackrabbit.mk.mem.JsopBuilder;
-import org.apache.jackrabbit.mk.mem.JsopTokenizer;
 import org.apache.jackrabbit.mk.store.Commit;
 import org.apache.jackrabbit.mk.store.CommitBuilder;
 import org.apache.jackrabbit.mk.store.Node;
@@ -439,7 +438,7 @@ public class MicroKernelImpl implements 
             if (! (entry.getValue() instanceof JSONObject)) {
                 Object o = entry.getValue();
                 if (o instanceof String) {
-                    o = JsopTokenizer.encode(o.toString());
+                    o = JsopBuilder.encode(o.toString());
                 }
                 props.put((String) entry.getKey(), o.toString());
             }
@@ -459,14 +458,14 @@ public class MicroKernelImpl implements 
     static class JSONArray extends LinkedList {
         public String toString() {
             JsopBuilder buff = new JsopBuilder();
-            buff.beginArray();
+            buff.arrayBegin();
             for (Object o : this) {
                 if (o instanceof String) {
-                    o = JsopTokenizer.encode(o.toString());
+                    o = JsopBuilder.encode(o.toString());
                 }
                 buff.value(o.toString());
             }
-            buff.endArray();
+            buff.arrayEnd();
             return buff.toString();
         }
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java
Fri Jun 10 10:50:08 2011
@@ -274,6 +274,10 @@ public final class JsonBuilder {
                     sb.append("\\t");
                     break;
                 case '/':
+                    // TODO why escape a slash? better not, so we can use the
+                    // data type hack described here:
+                    // http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx
+                    // http://stackoverflow.com/questions/206384/how-to-format-a-json-date
                     sb.append("\\/");
                     break;
                 default:
@@ -315,12 +319,14 @@ public final class JsonBuilder {
     }
 
     public static String encode(float value) {
+        // TODO silently losing data, should probably throw an exception instead
         return Float.isInfinite(value) || Float.isNaN(value)
             ? "null"
             : Float.toString(value);
     }
 
     public static String encode(double value) {
+        // TODO silently losing data, should probably throw an exception instead
         return Double.isInfinite(value) || Double.isNaN(value)
             ? "null"
             : Double.toString(value);

Copied: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java
(from r1134216, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopBuilder.java)
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java?p2=jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java&p1=jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopBuilder.java&r1=1134216&r2=1134274&rev=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopBuilder.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java
Fri Jun 10 10:50:08 2011
@@ -14,52 +14,82 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.mk.mem;
+package org.apache.jackrabbit.mk.json;
 
+import org.apache.jackrabbit.mk.mem.Constants;
+
+/**
+ * A builder for Json and Jsop strings. It encodes string values, and knows when
+ * a comma is needed.
+ */
 public class JsopBuilder {
 
     private StringBuilder buff = new StringBuilder();
     private boolean needComma;
 
-    public void beginObject() {
+    public JsopBuilder append(String token) {
+        buff.append(token);
+        needComma = false;
+        return this;
+    }
+
+    public JsopBuilder objectBegin() {
         optionalComma();
         buff.append('{');
         needComma = false;
+        return this;
     }
 
-    public void endObject() {
+    public JsopBuilder objectEnd() {
         if (Constants.JSON_NEWLINES) {
             buff.append("\n}");
         } else {
             buff.append('}');
         }
-        needComma = false;
+        needComma = true;
+        return this;
     }
 
-    public void beginArray() {
+    public JsopBuilder arrayBegin() {
         optionalComma();
         buff.append('[');
         needComma = false;
+        return this;
     }
 
-    public void endArray() {
+    public JsopBuilder arrayEnd() {
         buff.append("]");
         needComma = true;
+        return this;
     }
 
-    public void value(String encodedValue) {
-        optionalComma();
-        buff.append(encodedValue);
-        needComma = true;
-    }
-
-    public void property(String propertyName, String encodedValue) {
+    public JsopBuilder key(String propertyName) {
         optionalComma();
         if (Constants.JSON_NEWLINES) {
             buff.append('\n');
         }
-        buff.append(JsopTokenizer.encode(propertyName)).append(':').append(encodedValue);
+        buff.append(encode(propertyName)).append(':');
+        needComma = false;
+        return this;
+    }
+
+    public JsopBuilder value(long value) {
+        return encodedValue(Long.toString(value));
+    }
+
+    public JsopBuilder value(boolean value) {
+        return encodedValue(Boolean.toString(value));
+    }
+
+    public JsopBuilder value(String value) {
+        return encodedValue(encode(value));
+    }
+
+    public JsopBuilder encodedValue(String encodedValue) {
+        optionalComma();
+        buff.append(encodedValue);
         needComma = true;
+        return this;
     }
 
     private void optionalComma() {
@@ -72,4 +102,91 @@ public class JsopBuilder {
         return buff.toString();
     }
 
+    /**
+     * Convert a string to a quoted Json literal using the correct escape
+     * sequences. The literal is enclosed in double quotes. Characters outside
+     * the range 32..127 are encoded (backslash u xxxx). The forward slash
+     * (solidus) is not escaped. Null is encoded as "null" (without quotes).
+     *
+     * @param s the text to convert
+     * @return the Json representation (including double quotes)
+     */
+    public static String encode(String s) {
+        if (s == null) {
+            return "null";
+        }
+        int length = s.length();
+        if (length == 0) {
+            return "\"\"";
+        }
+        StringBuilder buff = new StringBuilder(length + 2);
+        buff.append('\"');
+        escape(s, length, buff);
+        return buff.append('\"').toString();
+    }
+
+    /**
+     * Escape a string into the target buffer.
+     *
+     * @param s the string to escape
+     * @param length the number of characters.
+     * @param buff the target buffer
+     */
+    public static void escape(String s, int length, StringBuilder buff) {
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            switch (c) {
+            case '"':
+                // quotation mark
+                buff.append("\\\"");
+                break;
+            case '\\':
+                // backslash
+                buff.append("\\\\");
+                break;
+            case '\b':
+                // backspace
+                buff.append("\\b");
+                break;
+            case '\f':
+                // formfeed
+                buff.append("\\f");
+                break;
+            case '\n':
+                // newline
+                buff.append("\\n");
+                break;
+            case '\r':
+                // carriage return
+                buff.append("\\r");
+                break;
+            case '\t':
+                // horizontal tab
+                buff.append("\\t");
+                break;
+            default:
+                int ch = c;
+                if (ch < ' ') {
+                    // guaranteed to be 1 or 2 hex digits only
+                    buff.append("\\u00");
+                    String hex = Integer.toHexString(c);
+                    if (hex.length() == 1) {
+                        buff.append('0');
+                    }
+                    buff.append(hex);
+                } else if (ch > 127) {
+                    // ascii only mode
+                    buff.append("\\u");
+                    String hex = Integer.toHexString(c);
+                    for (int len = hex.length(); len < 4; len++) {
+                        buff.append('0');
+                    }
+                    buff.append(hex);
+                } else {
+                    buff.append(c);
+                }
+            }
+        }
+    }
+
 }

Copied: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java
(from r1134215, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopTokenizer.java)
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java?p2=jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java&p1=jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopTokenizer.java&r1=1134215&r2=1134274&rev=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/JsopTokenizer.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java
Fri Jun 10 10:50:08 2011
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.mk.mem;
+package org.apache.jackrabbit.mk.json;
 
 /**
  * A tokenizer for Json and Jsop strings.
@@ -24,31 +24,30 @@ public class JsopTokenizer {
     // "- " is "patch remove", without space is a negative number
     // please note .0 and -.1 are not valid numbers
 
-    final static int STRING = 0, NUMBER = 1, TRUE = 2, FALSE = 3, NULL = 4;
-    final static int COMMENT = 5, ERROR = 6, END = 7;
+    public final static int STRING = 0, NUMBER = 1, TRUE = 2, FALSE = 3, NULL = 4;
+    public final static int COMMENT = 5, ERROR = 6, END = 7;
 
     private static final String[] TYPE = {
         "string", "number", "true", "false", "null", "error", "end"
     };
 
     private final String jsop;
+    private final int length;
     private int pos;
     private int currentType;
     private String currentToken;
     private int lastType;
     private String lastToken;
+    private String encodedString;
 
     public JsopTokenizer(String json) {
         this.jsop = json;
+        this.length = json.length();
         read();
     }
 
     public String toString() {
-        int todo;
-//      version as comment
-//      newlines in strings -> error
-//      project source code format
-
+        // TODO newlines in strings should probably throw an exception
         return jsop;
     }
 
@@ -129,10 +128,19 @@ public class JsopTokenizer {
         return lastType;
     }
 
+    /**
+     * Get the last encoded (raw) string, including escape sequences.
+     *
+     * @return the encoded string
+     */
+    public String lastEncodedString() {
+        return encodedString;
+    }
+
     private int readToken() {
         char ch;
         while (true) {
-            if (pos >= jsop.length()) {
+            if (pos >= length) {
                 return END;
             }
             ch = jsop.charAt(pos);
@@ -154,7 +162,7 @@ public class JsopTokenizer {
                     pos++;
                 }
             }
-            String s = jsop.substring(start + 1, pos - 1);
+            String s = encodedString = jsop.substring(start + 1, pos - 1);
             if (escaped) {
                 currentToken = decode(s);
             } else {
@@ -197,7 +205,7 @@ public class JsopTokenizer {
             // else fall though
         default:
             if (ch >= '0' && ch <= '9') {
-                while (pos < jsop.length()) {
+                while (pos < length) {
                     ch = jsop.charAt(pos);
                     if (ch < '0' || ch > '9') {
                         break;
@@ -206,7 +214,7 @@ public class JsopTokenizer {
                 }
                 if (ch == '.') {
                     pos++;
-                    while (pos < jsop.length()) {
+                    while (pos < length) {
                         ch = jsop.charAt(pos);
                         if (ch < '0' || ch > '9') {
                             break;
@@ -219,7 +227,7 @@ public class JsopTokenizer {
                     if (ch == '+' || ch == '-') {
                         ch = jsop.charAt(++pos);
                     }
-                    while (pos < jsop.length()) {
+                    while (pos < length) {
                         ch = jsop.charAt(pos);
                         if (ch < '0' || ch > '9') {
                             break;
@@ -230,7 +238,7 @@ public class JsopTokenizer {
                 currentToken = jsop.substring(start, pos);
                 return NUMBER;
             } else if (ch >= 'a' && ch <= 'z') {
-                while (pos < jsop.length()) {
+                while (pos < length) {
                     ch = jsop.charAt(pos);
                     if (ch < 'a' || ch > 'z') {
                         break;
@@ -257,7 +265,7 @@ public class JsopTokenizer {
     /**
      * Decode a Json string.
      *
-     * @param s the encoded string
+     * @param s the encoded string, without double quotes
      * @return the string
      */
     public static String decode(String s) {
@@ -315,79 +323,6 @@ public class JsopTokenizer {
         return buff.toString();
     }
 
-    /**
-     * Convert a string to a Json literal using the correct escape sequences.
-     * The literal is enclosed in double quotes. Characters outside the range
-     * 32..127 are encoded (backslash u xxxx). The forward slash (solidus) is
-     * not escaped.
-     *
-     * @param s the text to convert
-     * @return the Json representation (including double quotes)
-     */
-    public static String encode(String s) {
-        int length = s.length();
-        if (length == 0) {
-            return "\"\"";
-        }
-        StringBuilder buff = new StringBuilder(length + 2);
-        buff.append('\"');
-        for (int i = 0; i < length; i++) {
-            char c = s.charAt(i);
-            switch (c) {
-            case '"':
-                // quotation mark
-                buff.append("\\\"");
-                break;
-            case '\\':
-                // backslash
-                buff.append("\\\\");
-                break;
-            case '\b':
-                // backspace
-                buff.append("\\b");
-                break;
-            case '\f':
-                // formfeed
-                buff.append("\\f");
-                break;
-            case '\n':
-                // newline
-                buff.append("\\n");
-                break;
-            case '\r':
-                // carriage return
-                buff.append("\\r");
-                break;
-            case '\t':
-                // horizontal tab
-                buff.append("\\t");
-                break;
-            default:
-                int ch = c;
-                if (ch < ' ') {
-                    // guaranteed to be 1 or 2 hex digits only
-                    buff.append("\\u00");
-                    String hex = Integer.toHexString(c);
-                    if (hex.length() == 1) {
-                        buff.append('0');
-                    }
-                    buff.append(hex);
-                } else if (ch > 127) {
-                    // ascii only mode
-                    buff.append("\\u");
-                    String hex = Integer.toHexString(c);
-                    for (int len = hex.length(); len < 4; len++) {
-                        buff.append('0');
-                    }
-                    buff.append(hex);
-                } else {
-                    buff.append(c);
-                }
-            }
-        }
-        return buff.append('\"').toString();
-    }
-
     private static String getTokenType(int type) {
         return type <= END ? TYPE[type] : "'" + (char) type + "'";
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
Fri Jun 10 10:50:08 2011
@@ -20,6 +20,7 @@ import java.io.InputStream;
 import java.util.HashMap;
 import java.util.TreeMap;
 import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
 
 /**
  * An in-memory implementation.
@@ -67,6 +68,13 @@ public class MemoryKernelImpl implements
         // would be possible if we don't (always) return the head revision
         // TODO the hidden property ":name" prevents shareable nodes
         // TODO property type as comment versus as special property
+        // TODO metadata in storage (version)
+        // TODO optional read / write version in json 'api' (as comments?)
+        // TODO data type encoding mechanism:
+        // http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx
+        // http://stackoverflow.com/questions/206384/how-to-format-a-json-date
+        // String as Json: "/Date(1)/"; date as Json: "\/Date(1)\/"
+
         headRevId++;
         apply(path, jsonDiff);
         commit();

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
(original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
Fri Jun 10 10:50:08 2011
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map.Entry;
+import org.apache.jackrabbit.mk.json.JsopBuilder;
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
 
 public class NodeImpl {
 
@@ -145,27 +147,27 @@ public class NodeImpl {
     public String toString(boolean includeProperties, int depth, int childOffset, int childCount)
{
         JsopBuilder json = new JsopBuilder();
         if (includeProperties) {
-            json.beginObject();
+            json.objectBegin();
             if (properties != null) {
                 for (Entry<String, Val> e : properties.entrySet()) {
-                    json.property(e.getKey(), e.getValue().toString());
+                    json.key(e.getKey()).encodedValue(e.getValue().toString());
                 }
             }
         }
         if (childNodes != null) {
-            json.property(":childNodeCount", "" + childNodes.size());
+            json.key(":childNodeCount").value(childNodes.size());
             if (depth > 0) {
                 for (Entry<String, NodeImpl> e : childNodes.entrySet()) {
-                    json.property(e.getKey(), e.getValue().toString(true, depth - 1, -1,
-1));
+                    json.key(e.getKey()).encodedValue(e.getValue().toString(true, depth -
1, -1, -1));
                 }
             }
         } else {
             if (Constants.CHILD_NODE_COUNT_0) {
-                json.property(":childNodeCount", "0");
+                json.key(":childNodeCount").value(0);
             }
         }
         if (includeProperties) {
-            json.endObject();
+            json.objectEnd();
         }
         return json.toString();
     }
@@ -256,7 +258,17 @@ public class NodeImpl {
             int type = PropertyType.valueFromName(typeName);
             return Val.get(type, t.readString());
         }
-        return Val.get(t.readString());
+        String s = t.readString();
+        String encoded = t.lastEncodedString();
+        if (!encoded.startsWith("\\/")) {
+            return Val.get(s);
+        }
+        s = encoded.substring(2);
+        if (s.startsWith("NaN")) {
+            return Val.get(Double.NaN);
+        }
+        return Val.get(s);
+
     }
 
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Val.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Val.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Val.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Val.java Fri
Jun 10 10:50:08 2011
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.mk.mem;
 
 import java.util.Arrays;
 import java.util.HashMap;
+import org.apache.jackrabbit.mk.json.JsopBuilder;
+import org.apache.jackrabbit.mk.util.StringUtils;
 
 /**
  * A value.
@@ -291,34 +293,51 @@ public class Val implements Comparable<V
         }
         switch (type) {
         case TYPE_MULTI_VALUE: {
-            StringBuilder buff = new StringBuilder("[ ");
-            int i = 0;
+            JsopBuilder buff = new JsopBuilder();
+            buff.arrayBegin();
             for (Val v : (Val[]) value) {
-                if (i++ > 0) {
-                    buff.append(", ");
-                }
                 // should in theory never be null (unless there is a bug)
                 if (v != null) {
-                    buff.append(v.toString());
+                    buff.encodedValue(v.toString());
                 }
             }
-            return buff.append(" ]").toString();
-        }
-        case TYPE_BINARY_REFERENCE: {
-            return "\"" + value + "\"";
+            return buff.arrayEnd().toString();
         }
         case PropertyType.BOOLEAN:
         case PropertyType.LONG:
-        case PropertyType.DECIMAL:
             return value.toString();
-        case PropertyType.DOUBLE:
+        case PropertyType.DECIMAL: {
             String s = value.toString();
-            if (s.equals("NaN") || s.endsWith("Infinity")) {
-                s = "\"" + s + "\"";
+            if (s.indexOf('.') < 0) {
+                return s + ".";
             }
             return s;
-        default:
-            return JsopTokenizer.encode(value.toString());
+        }
+        case PropertyType.DOUBLE: {
+            String s = value.toString();
+            if (s.equals("NaN") || s.endsWith("Infinity")) {
+                return "\"\\/" + s + "\\/\"";
+            } else {
+                return "\"\\/Double(" + s + ")\\/\"";
+            }
+        }
+        case PropertyType.BINARY: {
+            byte[] bytes = getBytes();
+            return "\"\\/Bytes(\\\"" + StringUtils.convertBytesToHex(bytes) + "\\\")\\/\"";
+        }
+        case PropertyType.STRING:
+            return JsopBuilder.encode(value.toString());
+        default: {
+            String s = value.toString();
+            int length = s.length();
+            StringBuilder buff = new StringBuilder(length + 5);
+            buff.append("\"\\/").
+                append(PropertyType.nameFromValue(type)).
+                append("(\\\"");
+            JsopBuilder.escape(s, length, buff);
+            buff.append("\\\")\\/\"");
+            return buff.toString();
+        }
         }
     }
 
@@ -353,9 +372,9 @@ public class Val implements Comparable<V
             }
             return "/* " + PropertyType.nameFromValue(type) + " */ " + s;
         case PropertyType.STRING:
-            return JsopTokenizer.encode(value.toString());
+            return JsopBuilder.encode(value.toString());
         default:
-            return "/* " + PropertyType.nameFromValue(type) + " */ " + JsopTokenizer.encode(value.toString());
+            return "/* " + PropertyType.nameFromValue(type) + " */ " + JsopBuilder.encode(value.toString());
         }
     }
 

Modified: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java?rev=1134274&r1=1134273&r2=1134274&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java
(original)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java
Fri Jun 10 10:50:08 2011
@@ -42,7 +42,7 @@ public class HelloWorld {
             head = mk.commit("/", "-\"test\"", head);
         }
 
-        head = mk.commit("/", "+\"foo\" : {\"bar\":{\"x\":1}}", head);
+        head = mk.commit("/", "+\"foo\" : {\"bar\":{\"x\":\"\\/NaN\\/\"}}", head);
         String s = mk.getNodes("/foo/bar", 0, head);
         System.out.println(s);
 

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java?rev=1134274&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java
(added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java
Fri Jun 10 10:50:08 2011
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.jackrabbit.mk.json;
+
+import java.io.IOException;
+import junit.framework.TestCase;
+
+/**
+ * Test the Jsop tokenizer and builder.
+ */
+public class JsopTest extends TestCase {
+
+    public void testDataType() {
+        String dateString = new JsopBuilder().
+            key("string").value("/Date(0)/").
+            key("date").encodedValue("\"\\/Date(0)\\/\"").
+            toString();
+        assertEquals(
+                "\"string\":\"/Date(0)/\"," +
+                "\"date\":\"\\/Date(0)\\/\"",
+                dateString);
+        JsopTokenizer t = new JsopTokenizer(dateString);
+        assertEquals("string", t.readString());
+        t.read(':');
+        assertEquals("/Date(0)/", t.readString());
+        assertEquals("/Date(0)/", t.lastEncodedString());
+        t.read(',');
+        assertEquals("date", t.readString());
+        t.read(':');
+        assertEquals("/Date(0)/", t.readString());
+        assertEquals("\\/Date(0)\\/", t.lastEncodedString());
+    }
+
+    public void testTokenizer() {
+        try {
+            JsopTokenizer.decode("test\\");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            JsopTokenizer.decode("wrong\\uxxxx");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            JsopTokenizer.decode("wrong\\m");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        test("/error/", "\"\\");
+        test("/error/1", ".1");
+        assertEquals("x", new JsopTokenizer("x").toString());
+        test("/error/", "true" + "true");
+        test("/error/", "truer");
+        test("/error/", "falsehood");
+        test("/error/", "nil");
+        test("/error/1", "nil 1");
+        test("/error/", "\"invalid");
+        test("- \"test/test\"", "-\"test\\/test\"");
+        test(" {\n\"x\": 1, \"y\": 2\n}\n", "{\"x\":1, \"y\":2}");
+        test(" [\ntrue, false, null\n]\n", "[true, false, null]");
+        test("\"\"", "\"\"");
+        test("\"\\u0003\"", "\"\\u0003\"");
+        test("\"\\u0012\"", "\"\\u0012\"");
+        test("\"\\u0123\"", "\"\\u0123\"");
+        test("\"\\u1234\"", "\"\\u1234\"");
+        test("\"-\\\\-\\\"-\\b-\\f-\\n-\\r-\\t\"", "\"-\\\\-\\\"-\\b-\\f-\\n-\\r-\\t\"");
+        test("\"-\\b-\\f-\\n-\\r-\\t\"", "\"-\b-\f-\n-\r-\t\"");
+        test(" [\n0, 12, -1, 0.1, -0.1, -2.3e1, 1e+1, 1.e-20\n]\n", "[0,12,-1,0.1,-0.1,-2.3e1,1e+1,1.e-20]");
+        test("\"Hello\"", "\"Hello\"");
+        test(" [\n\n]\n", "[]");
+        test(" {\n\n}\n", "{}");
+        test(" {\n\"a\": /* test */ 10\n}\n", "{ \"a\": /* test */ 10}");
+        test("+ - / ^ ", "+ - / ^");
+
+        JsopTokenizer t = new JsopTokenizer("{}123");
+        assertFalse(t.matches('+'));
+        assertTrue(t.matches('{'));
+        t.read('}');
+
+        try {
+            t.read('+');
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals("{}123[*] expected: '+'", e.getMessage());
+        }
+        try {
+            t.read(JsopTokenizer.STRING);
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertEquals("{}123[*] expected: string", e.getMessage());
+        }
+
+    }
+
+    static void test(String expected, String json) {
+        String j2 = prettyPrintWithErrors(json);
+        assertEquals(expected, j2);
+    }
+
+    static String prettyPrintWithErrors(String jsop) {
+        StringBuilder buff = new StringBuilder();
+        JsopTokenizer t = new JsopTokenizer(jsop);
+        while (true) {
+            prettyPrint(buff, t);
+            if (t.getTokenType() == JsopTokenizer.END) {
+                return buff.toString();
+            }
+        }
+    }
+
+    static String prettyPrint(StringBuilder buff, JsopTokenizer t) {
+        while (true) {
+            switch (t.read()) {
+            case JsopTokenizer.END:
+                return buff.toString();
+            case JsopTokenizer.STRING:
+                buff.append(JsopBuilder.encode(t.getToken()));
+                break;
+            case JsopTokenizer.NUMBER:
+                buff.append(t.getToken());
+                break;
+            case JsopTokenizer.TRUE:
+                buff.append("true");
+                break;
+            case JsopTokenizer.FALSE:
+                buff.append("false");
+                break;
+            case JsopTokenizer.NULL:
+                buff.append("null");
+                break;
+            case JsopTokenizer.ERROR:
+                buff.append("/error/");
+                break;
+            case JsopTokenizer.COMMENT:
+                buff.append("/*").append(t.getToken()).append("*/ ");
+                break;
+            case '{':
+                buff.append(" {\n");
+                break;
+            case '}':
+                buff.append("\n}\n");
+                break;
+            case '[':
+                buff.append(" [\n");
+                break;
+            case ']':
+                buff.append("\n]\n");
+                break;
+            case ',':
+                buff.append(", ");
+                break;
+            case ':':
+                buff.append(": ");
+                break;
+            case '+':
+                buff.append("+ ");
+                break;
+            case '-':
+                buff.append("- ");
+                break;
+            case '^':
+                buff.append("^ ");
+                break;
+            case '/':
+                buff.append("/ ");
+                break;
+            default:
+                throw new AssertionError("token type: " + t.getTokenType());
+            }
+        }
+    }
+
+    public void testBuilder() throws IOException {
+
+        JsopBuilder buff = new JsopBuilder();
+        buff.append("+ ").objectBegin().
+            key("foo").value("bar").
+            key("int").value(3).
+            key("decimal").encodedValue("3.0").
+            key("obj").objectBegin().
+                key("boolean").value(true).
+                key("null").value(null).
+                key("arr").arrayBegin().
+                    value(1).
+                    value("42").
+                arrayEnd().
+            objectEnd().
+            key("some").value("more").
+        objectEnd();
+
+        String json = buff.toString();
+        assertEquals("+ {\"foo\":\"bar\",\"int\":3,\"decimal\":3.0," +
+                "\"obj\":{\"boolean\":true,\"null\":null," +
+                "\"arr\":[1,\"42\"]},\"some\":\"more\"}", json);
+    }
+
+    public void testEscape() throws IOException {
+        assertEquals("null", JsopBuilder.encode(null));
+        JsopBuilder buff = new JsopBuilder().
+            key("back\\slash").value("\\").
+            key("back\\\\slash").value("\\\\");
+        assertEquals("\"back\\\\slash\":\"\\\\\",\"back\\\\\\\\slash\":\"\\\\\\\\\"", buff.toString());
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/ValTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/ValTest.java?rev=1134274&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/ValTest.java
(added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/ValTest.java
Fri Jun 10 10:50:08 2011
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.jackrabbit.mk.json;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.mk.mem.PropertyType;
+import org.apache.jackrabbit.mk.mem.Val;
+
+/**
+ * Test the Val class.
+ */
+public class ValTest extends TestCase {
+
+    public void testJson() {
+        assertEquals("true", Val.get(true).toString());
+        assertEquals("false", Val.get(false).toString());
+        assertEquals("1", Val.get(1).toString());
+        assertEquals("10.0", Val.get(PropertyType.DECIMAL, "10.0").toString());
+        assertEquals("10.", Val.get(PropertyType.DECIMAL, "10").toString());
+        assertEquals("\"\\/Double(10.0)\\/\"", Val.get(10.0).toString());
+        assertEquals("\"\\/NaN\\/\"", Val.get(Double.NaN).toString());
+        assertEquals("\"Hello\"", Val.get("Hello").toString());
+        assertEquals("\"\\/Bytes(\\\"\\\")\\/\"", Val.get(new byte[0]).toString());
+        assertEquals("\"\\/Bytes(\\\"ff\\\")\\/\"", Val.get(new byte[]{(byte) 255}).toString());
+    }
+
+}



Mime
View raw message