Author: jukka
Date: Sat Oct 19 00:01:36 2013
New Revision: 1533666

URL: http://svn.apache.org/r1533666
Log:
OAK-987: Implement the MicroKernel API

Track revision history in NodeStoreKernel
Add filter support to getNodes()
Better handling of property values in JSON/P

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/JsopDiffTest.java

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java?rev=1533666&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java
 Sat Oct 19 00:01:36 2013
@@ -0,0 +1,130 @@
+/*
+ * 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.oak.kernel;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.jackrabbit.mk.json.JsopTokenizer;
+
+/**
+ * Utility class for deciding which nodes and properties to serialize.
+ */
+class JsonFilter {
+
+    private static final Pattern EVERYTHING = Pattern.compile(".*");
+
+    private final List<Pattern> nodeIncludes = newArrayList(EVERYTHING);
+
+    private final List<Pattern> nodeExcludes = newArrayList();
+
+    private final List<Pattern> propertyIncludes = newArrayList(EVERYTHING);
+
+    private final List<Pattern> propertyExcludes = newArrayList();
+
+    JsonFilter(String filter) {
+        JsopTokenizer tokenizer = new JsopTokenizer(filter);
+        tokenizer.read('{');
+        for (boolean first = true; !tokenizer.matches('}'); first = false) {
+            if (!first) {
+                tokenizer.read(',');
+            }
+            String key = tokenizer.readString();
+            tokenizer.read(':');
+
+            List<Pattern> includes = newArrayList();
+            List<Pattern> excludes = newArrayList();
+            readPatterns(tokenizer, includes, excludes);
+
+            if (key.equals("nodes")) {
+                nodeIncludes.clear();
+                nodeIncludes.addAll(includes);
+                nodeExcludes.clear();
+                nodeExcludes.addAll(excludes);
+            } else if (key.equals("properties")) {
+                propertyIncludes.clear();
+                propertyIncludes.addAll(includes);
+                propertyExcludes.clear();
+                propertyExcludes.addAll(excludes);
+            } else {
+                throw new IllegalStateException(key);
+            }
+        }
+    }
+
+    private void readPatterns(JsopTokenizer tokenizer, List<Pattern> includes,
+            List<Pattern> excludes) {
+        tokenizer.read('[');
+        for (boolean first = true; !tokenizer.matches(']'); first = false) {
+            if (!first) {
+                tokenizer.read(',');
+            }
+            String pattern = tokenizer.readString();
+            if (pattern.startsWith("-")) {
+                excludes.add(glob(pattern.substring(1)));
+            } else if (pattern.startsWith("\\-")) {
+                includes.add(glob(pattern.substring(1)));
+            } else {
+                includes.add(glob(pattern));
+            }
+        }
+    }
+
+    private static Pattern glob(String pattern) {
+        StringBuilder builder = new StringBuilder();
+        int star = pattern.indexOf('*');
+        while (star != -1) {
+            if (star > 0 && pattern.charAt(star - 1) == '\\') {
+                builder.append(Pattern.quote(pattern.substring(0, star - 1)));
+                builder.append(Pattern.quote("*"));
+            } else {
+                builder.append(Pattern.quote(pattern.substring(0, star)));
+                builder.append(".*");
+            }
+            pattern = pattern.substring(star + 1);
+            star = pattern.indexOf('*');
+        }
+        builder.append(Pattern.quote(pattern));
+        return Pattern.compile(builder.toString());
+    }
+
+    boolean includeNode(String name) {
+        return include(name, nodeIncludes, nodeExcludes);
+    }
+
+    boolean includeProperty(String name) {
+        return include(name, propertyIncludes, propertyExcludes);
+    }
+
+    private boolean include(
+            String name, List<Pattern> includes, List<Pattern> excludes) {
+        for (Pattern include : includes) {
+            if (include.matcher(name).matches()) {
+                for (Pattern exclude : excludes) {
+                    if (exclude.matcher(name).matches()) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java?rev=1533666&r1=1533665&r2=1533666&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
 Sat Oct 19 00:01:36 2013
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.kernel
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.jackrabbit.oak.api.Type.BINARY;
 import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
+import static org.apache.jackrabbit.oak.api.Type.DOUBLE;
 import static org.apache.jackrabbit.oak.api.Type.LONG;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
 
@@ -28,7 +29,6 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.memory.AbstractBlob;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -45,45 +45,51 @@ class JsonSerializer {
 
     private final int maxChildNodes;
 
-    private final boolean includeChildNodeCount;
+    private final JsonFilter filter;
 
     private final BlobSerializer blobs;
 
     private JsonSerializer(
             JsopBuilder json, int depth, long offset, int maxChildNodes,
-            boolean includeChildNodeCount, BlobSerializer blobs) {
+            JsonFilter filter, BlobSerializer blobs) {
         this.json = checkNotNull(json);
         this.depth = depth;
         this.offset = offset;
         this.maxChildNodes = maxChildNodes;
-        this.includeChildNodeCount = includeChildNodeCount;
+        this.filter = checkNotNull(filter);
         this.blobs = checkNotNull(blobs);
     }
 
     JsonSerializer(
-            int depth, long offset, int maxChildNodes, BlobSerializer blobs) {
-        this(new JsopBuilder(), depth, offset, maxChildNodes, true, blobs);
+            int depth, long offset, int maxChildNodes,
+            String filter, BlobSerializer blobs) {
+        this(new JsopBuilder(), depth, offset, maxChildNodes,
+                new JsonFilter(filter), blobs);
     }
 
     JsonSerializer(JsopBuilder json, BlobSerializer blobs) {
-        this(json, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, false, blobs);
+        this(json, Integer.MAX_VALUE, 0, Integer.MAX_VALUE,
+                new JsonFilter("{\"properties\":[\"*\", 
\"-:childNodeCount\"]}"),
+                blobs);
     }
 
     protected JsonSerializer getChildSerializer() {
         return new JsonSerializer(
-                json, depth - 1, 0, maxChildNodes,
-                includeChildNodeCount, blobs);
+                json, depth - 1, 0, maxChildNodes, filter, blobs);
     }
 
     void serialize(NodeState node) {
         json.object();
 
         for (PropertyState property : node.getProperties()) {
-            json.key(property.getName());
-            serialize(property);
+            String name = property.getName();
+            if (filter.includeProperty(name)) {
+                json.key(name);
+                serialize(property);
+            }
         }
 
-        if (includeChildNodeCount) {
+        if (filter.includeProperty(":childNodeCount")) {
             json.key(":childNodeCount");
             json.value(node.getChildNodeCount(Integer.MAX_VALUE));
         }
@@ -91,12 +97,13 @@ class JsonSerializer {
         int index = 0;
         int count = 0;
         for (ChildNodeEntry child : node.getChildNodeEntries()) {
-            if (index++ >= offset) {
+            String name = child.getName();
+            if (filter.includeNode(name) && index++ >= offset) {
                 if (count++ >= maxChildNodes) {
                     break;
                 }
 
-                json.key(child.getName());
+                json.key(name);
                 if (depth > 0) {
                     getChildSerializer().serialize(child.getNodeState());
                 } else {
@@ -135,6 +142,13 @@ class JsonSerializer {
             json.value(property.getValue(BOOLEAN, index).booleanValue());
         } else if (type == LONG) {
             json.value(property.getValue(LONG, index).longValue());
+        } else if (type == DOUBLE) {
+            Double value = property.getValue(DOUBLE, index);
+            if (value.isNaN() || value.isInfinite()) {
+                json.value(TypeCodes.encode(type.tag(), value.toString()));
+            } else {
+                json.encodedValue(value.toString());
+            }
         } else if (type == BINARY) {
             Blob blob = property.getValue(BINARY, index);
             json.value(TypeCodes.encode(type.tag(), blobs.serialize(blob)));

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java?rev=1533666&r1=1533665&r2=1533666&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
 Sat Oct 19 00:01:36 2013
@@ -44,8 +44,12 @@ public class JsopDiff implements NodeSta
     }
 
 
+    JsopDiff(BlobSerializer blobs, String path) {
+        this(new JsopBuilder(), blobs, path, Integer.MAX_VALUE);
+    }
+
     JsopDiff(BlobSerializer blobs) {
-        this(new JsopBuilder(), blobs, "/", Integer.MAX_VALUE);
+        this(blobs, "/");
     }
 
     JsopDiff(String path, int depth) {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java?rev=1533666&r1=1533665&r2=1533666&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
 Sat Oct 19 00:01:36 2013
@@ -42,6 +42,7 @@ import com.google.common.cache.CacheLoad
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.mk.json.JsopReader;
@@ -51,7 +52,9 @@ import org.apache.jackrabbit.oak.api.Typ
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
@@ -751,7 +754,13 @@ public final class KernelNodeState exten
     private PropertyState readProperty(String name, JsopReader reader) {
         if (reader.matches(JsopReader.NUMBER)) {
             String number = reader.getToken();
-            return createProperty(name, number, PropertyType.LONG);
+            try {
+                return new LongPropertyState(
+                        name, Long.parseLong(number), Type.LONG);
+            } catch (NumberFormatException e) {
+                return new DoublePropertyState(
+                        name, Double.parseDouble(number));
+            }
         } else if (reader.matches(JsopReader.TRUE)) {
             return BooleanPropertyState.booleanProperty(name, true);
         } else if (reader.matches(JsopReader.FALSE)) {
@@ -795,8 +804,13 @@ public final class KernelNodeState exten
         while (!reader.matches(']')) {
             if (reader.matches(JsopReader.NUMBER)) {
                 String number = reader.getToken();
-                type = PropertyType.LONG;
-                values.add(Conversions.convert(number).toLong());
+                try {
+                    type = PropertyType.LONG;
+                    values.add(Long.parseLong(number));
+                } catch (NumberFormatException e) {
+                    type = PropertyType.DOUBLE;
+                    values.add(Double.parseDouble(number));
+                }
             } else if (reader.matches(JsopReader.TRUE)) {
                 type = PropertyType.BOOLEAN;
                 values.add(true);

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java?rev=1533666&r1=1533665&r2=1533666&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
 Sat Oct 19 00:01:36 2013
@@ -58,7 +58,7 @@ public class NodeStoreKernel implements 
 
     private final NodeStore store;
 
-    private final Map<String, NodeState> revisions = newLinkedHashMap();
+    private final Map<String, Revision> revisions = newLinkedHashMap();
 
     private final Map<String, NodeBuilder> branches = newLinkedHashMap();
 
@@ -78,7 +78,7 @@ public class NodeStoreKernel implements 
     public NodeStoreKernel(NodeStore store) {
         this.store = store;
         this.head = UUID.randomUUID().toString();
-        revisions.put(head, store.getRoot());
+        revisions.put(head, new Revision(store.getRoot()));
     }
 
     private synchronized NodeState getRoot(String revision)
@@ -87,9 +87,9 @@ public class NodeStoreKernel implements 
             revision = head;
         }
 
-        NodeState root = revisions.get(revision);
-        if (root != null) {
-            return root;
+        Revision r = revisions.get(revision);
+        if (r != null) {
+            return r.root;
         }
 
         NodeBuilder builder = branches.get(revision);
@@ -256,9 +256,9 @@ public class NodeStoreKernel implements 
     @Override
     public synchronized String getHeadRevision() {
         NodeState root = store.getRoot();
-        if (!root.equals(revisions.get(head))) {
+        if (!root.equals(revisions.get(head).root)) {
             head = UUID.randomUUID().toString();
-            revisions.put(head, root);
+            revisions.put(head, new Revision(root, "external"));
             notifyAll();
         }
         return head;
@@ -273,21 +273,49 @@ public class NodeStoreKernel implements 
     public synchronized String getRevisionHistory(
             long since, int maxEntries, String path)
             throws MicroKernelException {
+        if (maxEntries < 0) {
+            maxEntries = Integer.MAX_VALUE;
+        }
+
         JsopBuilder json = new JsopBuilder();
         json.array();
         int count = 0;
-        for (String revision : revisions.keySet()) {
-            if (count++ > maxEntries) {
-                break;
+        NodeState before = null;
+        for (Map.Entry<String, Revision> entry : revisions.entrySet()) {
+            Revision rev = entry.getValue();
+            if (before != null && rev.timestamp >= since
+                    && !getPathChanges(before, rev.root, path).isEmpty()) {
+                if (count++ > maxEntries) {
+                    break;
+                }
+                json.object();
+                json.key("id").value(entry.getKey());
+                json.key("ts").value(rev.timestamp);
+                json.key("msg").value(rev.message);
+                json.endObject();
             }
-            json.object();
-            json.key("id").value(revision);
-            json.endObject();
+            before = rev.root;
         }
         json.endArray();
         return json.toString();
     }
 
+    private String getPathChanges(
+            NodeState before, NodeState after, String path) {
+        if (path != null) {
+            for (String element : PathUtils.elements(path)) {
+                before = before.getChildNode(element);
+                after = after.getChildNode(element);
+            }
+        } else {
+            path = "/";
+        }
+
+        JsopDiff diff = new JsopDiff(blobSerializer, path);
+        after.compareAgainstBaseState(before, diff);
+        return diff.toString();
+    }
+
     @Override
     public synchronized String waitForCommit(
             String oldHeadRevisionId, long timeout)
@@ -301,10 +329,41 @@ public class NodeStoreKernel implements 
     }
 
     @Override
-    public String getJournal(
+    public synchronized String getJournal(
             String fromRevisionId, String toRevisionId, String path)
             throws MicroKernelException {
-        throw new UnsupportedOperationException();
+        if (!revisions.containsKey(fromRevisionId)) {
+            throw new  MicroKernelException(
+                    "Revision not found: " + fromRevisionId);
+        }
+
+        JsopBuilder json = new JsopBuilder();
+        json.array();
+        NodeState before = null;
+        boolean active = false;
+        for (Map.Entry<String, Revision> entry : revisions.entrySet()) {
+            String id = entry.getKey();
+            Revision rev = entry.getValue();
+            active = active || id.equals(fromRevisionId);
+            if (before != null && active) {
+                String jsop = getPathChanges(before, rev.root, path);
+                if (!jsop.isEmpty()) {
+                    json.object();
+                    json.key("id").value(id);
+                    json.key("ts").value(rev.timestamp);
+                    json.key("msg").value(rev.message);
+                    json.key("changes").value(jsop);
+                    json.endObject();
+                }
+            }
+            if (id.equals(toRevisionId)) {
+                break;
+            }
+            before = rev.root;
+        }
+
+        json.endArray();
+        return json.toString();
     }
 
     @Override
@@ -364,8 +423,11 @@ public class NodeStoreKernel implements 
             if (maxChildNodes < 0) {
                 maxChildNodes = Integer.MAX_VALUE;
             }
+            if (filter == null) {
+                filter = "{}";
+            }
             JsonSerializer json = new JsonSerializer(
-                    depth, offset, maxChildNodes, blobSerializer);
+                    depth, offset, maxChildNodes, filter, blobSerializer);
             json.serialize(node);
             return json.toString();
         } else {
@@ -381,19 +443,20 @@ public class NodeStoreKernel implements 
             revisionId = head;
         }
 
-        NodeState root = revisions.get(revisionId);
-        if (root != null) {
+        Revision r = revisions.get(revisionId);
+        if (r != null) {
             try {
-                NodeBuilder builder = root.builder();
+                NodeBuilder builder = r.root.builder();
                 applyJsop(builder, path, jsonDiff);
                 NodeState newRoot = store.merge(
                         builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
 
-                NodeState oldRoot = revisions.get(head);
-                if (!newRoot.equals(oldRoot)) {
+                Revision old = revisions.get(head);
+                if (!newRoot.equals(old.root)) {
                     String uuid = UUID.randomUUID().toString();
-                    revisions.put(uuid, newRoot);
+                    revisions.put(uuid, new Revision(newRoot, message));
                     head = uuid;
+                    notifyAll();
                 }
 
                 return head;
@@ -430,11 +493,12 @@ public class NodeStoreKernel implements 
                 NodeState newRoot = store.merge(
                         builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
 
-                NodeState oldRoot = revisions.get(head);
-                if (!newRoot.equals(oldRoot)) {
+                Revision old = revisions.get(head);
+                if (!newRoot.equals(old.root)) {
                     String uuid = UUID.randomUUID().toString();
-                    revisions.put(uuid, newRoot);
+                    revisions.put(uuid, new Revision(newRoot, message));
                     head = uuid;
+                    notifyAll();
                 }
 
                 return head;
@@ -532,4 +596,26 @@ public class NodeStoreKernel implements 
         }
     }
 
+    private static class Revision {
+
+        private final NodeState root;
+
+        private final String message;
+
+        private final long timestamp;
+
+        Revision(NodeState root, String message) {
+            this.root = root;
+            this.message = message;
+            this.timestamp = System.currentTimeMillis();
+        }
+
+        Revision(NodeState root) {
+            this.root = root;
+            this.message = "origin";
+            this.timestamp = -1;
+        }
+
+    }
+
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/JsopDiffTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/JsopDiffTest.java?rev=1533666&r1=1533665&r2=1533666&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/JsopDiffTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/JsopDiffTest.java
 Sat Oct 19 00:01:36 2013
@@ -45,7 +45,7 @@ public class JsopDiffTest {
 
         diff = new JsopDiff();
         diff.propertyChanged(before, DoublePropertyState.doubleProperty("foo", 
1.23));
-        assertEquals("^\"/foo\":\"dou:1.23\"", diff.toString()); // TODO: 1.23?
+        assertEquals("^\"/foo\":1.23", diff.toString());
 
         diff = new JsopDiff();
         diff.propertyChanged(before, 
BooleanPropertyState.booleanProperty("foo", true));


Reply via email to