jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject svn commit: r1161434 [1/3] - in /jackrabbit/sandbox/microkernel: ./ src/main/java/org/apache/jackrabbit/mk/ src/main/java/org/apache/jackrabbit/mk/api/ src/main/java/org/apache/jackrabbit/mk/blobs/ src/main/java/org/apache/jackrabbit/mk/datastore/ src/...
Date Thu, 25 Aug 2011 08:18:59 GMT
Author: stefan
Date: Thu Aug 25 08:18:57 2011
New Revision: 1161434

URL: http://svn.apache.org/viewvc?rev=1161434&view=rev
Log:
some refactoring: extracting blob handling interface from loose object store interface

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/
      - copied from r1159622, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/datastore/
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/PersistenceManager.java
      - copied, changed from r1159622, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/ObjectStore.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/
      - copied from r1159622, jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/datastore/
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DbBlobStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/FileBlobStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStoreTest.java
Removed:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/Store.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/StoreInputStream.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/datastore/
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/DataStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/ObjectStore.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DbStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/FileStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/MemoryStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/datastore/
Modified:
    jackrabbit/sandbox/microkernel/pom.xml
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.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/store/Blob.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Commit.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/CommitBuilder.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Constants.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/H2RevisionStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MutableCommit.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MutableNode.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Node.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/NodeDelta.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/NodeUtils.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/PersistentId.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/RevisionStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/SimpleRevisionStore.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/CommitGate.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/IOUtils.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/MicroKernelInputStream.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/PathUtils.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/SmallLRUCache.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringUtils.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/ConcurrentWriteTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/LargeObjectTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/Profiler.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DataStoreTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsonBuilderTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/CommitGateTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/IOUtilsTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/NonDescendingClockTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/PathTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/StringUtilsTest.java

Modified: jackrabbit/sandbox/microkernel/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/pom.xml?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/pom.xml (original)
+++ jackrabbit/sandbox/microkernel/pom.xml Thu Aug 25 08:18:57 2011
@@ -27,16 +27,9 @@
     <artifactId>microkernel</artifactId>
     <version>0.1-SNAPSHOT</version>
     <name>MicroKernel</name>
-    <description>Hierarchical MVCC-based persistence store</description>
+    <description>Hierarchical MVCC-based persistence blobStore</description>
 
     <dependencies>
-        <!-- json-simple library -->
-        <dependency>
-            <groupId>com.googlecode.json-simple</groupId>
-            <artifactId>json-simple</artifactId>
-            <version>1.1</version>
-        </dependency>
-
         <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
@@ -45,11 +38,18 @@
 
         <!--Test Dependencies-->
         <dependency>
-           <groupId>junit</groupId>
-           <artifactId>junit</artifactId>
-           <version>4.5</version>
-           <scope>test</scope>
-         </dependency>
+            <groupId>com.googlecode.json-simple</groupId>
+            <artifactId>json-simple</artifactId>
+            <version>1.1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.5</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -67,17 +67,17 @@
             -->
             <plugin>
                 <groupId>org.pitest</groupId>
-                    <artifactId>pitest-maven</artifactId>
-                    <version>0.20</version>
-                    <configuration>
-                        <inScopeClasses>
-                            <param>org.apache.jackrabbit.mk.util.Path*</param>
-                        </inScopeClasses>
-                        <targetClasses>
-                            <param>org.apache.jackrabbit.mk.util.Path*</param>
-                        </targetClasses>
-                    </configuration>
-                </plugin>      
+                <artifactId>pitest-maven</artifactId>
+                <version>0.20</version>
+                <configuration>
+                    <inScopeClasses>
+                        <param>org.apache.jackrabbit.mk.util.Path*</param>
+                    </inScopeClasses>
+                    <targetClasses>
+                        <param>org.apache.jackrabbit.mk.util.Path*</param>
+                    </targetClasses>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java Thu Aug 25 08:18:57 2011
@@ -41,7 +41,7 @@ public class Constants {
     public static final int MEM_MAP_ENTRY = 32;
     public static final int MEM_EVENT = 64;
     public static final int MEM_NODE_DATA = 48;
-    public static final int MEM_VAL_LONG =  32;
+    public static final int MEM_VAL_LONG = 32;
     public static final int MEM_VAL_BINARY = 32;
     public static final int MEM_VAL_STRING = 64;
     public static final int MEM_VAL_OBJ = 16;

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java Thu Aug 25 08:18:57 2011
@@ -16,10 +16,11 @@
  */
 package org.apache.jackrabbit.mk;
 
-import java.io.File;
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.mem.MemoryKernelImpl;
 
+import java.io.File;
+
 /**
  * A factory to create a MicroKernel instance.
  */

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=1161434&r1=1161433&r2=1161434&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 Thu Aug 25 08:18:57 2011
@@ -107,9 +107,9 @@ public class MicroKernelImpl implements 
         for (int i = history.size() - 1; i >= 0; i--) {
             Commit commit = history.get(i);
             buff.object().
-                key("id").value(commit.getId()).
-                key("ts").value(commit.getCommitTS()).
-            endObject();
+                    key("id").value(commit.getId()).
+                    key("ts").value(commit.getCommitTS()).
+                    endObject();
         }
         return buff.endArray().toString();
     }
@@ -150,33 +150,33 @@ public class MicroKernelImpl implements 
                 continue;
             }
             commitBuff.object().
-                key("id").value(commit.getId()).
-                key("ts").value(commit.getCommitTS()).
-                key("msg").value(commit.getMsg());
+                    key("id").value(commit.getId()).
+                    key("ts").value(commit.getCommitTS()).
+                    key("msg").value(commit.getMsg());
             final JsopBuilder buff = new JsopBuilder();
             // maps (key: id of target node, value: path/to/target)
             // for tracking added/removed nodes; this allows us
             // to detect 'move' operations
-            final HashMap<String, String> addedNodes =  new HashMap<String, String>();
-            final HashMap<String, String> removedNodes =  new HashMap<String, String>();
+            final HashMap<String, String> addedNodes = new HashMap<String, String>();
+            final HashMap<String, String> removedNodes = new HashMap<String, String>();
             try {
                 String path = "/";
                 Node node1 = rep.getNode(commit.getParentId(), path);
                 Node node2 = rep.getNode(commit.getId(), path);
 
-                NodeUtils.diff(path, node1, node2, true, rep.getStore(), new NodeDiffHandler() {
+                NodeUtils.diff(path, node1, node2, true, rep.getPersistenceManager(), new NodeDiffHandler() {
                     public void propAdded(String nodePath, String propName, String value) {
                         buff.append("+ ").
-                            key(PathUtils.concat(nodePath, propName)).
-                            encodedValue(value).
-                            appendWhitespace("\n");
+                                key(PathUtils.concat(nodePath, propName)).
+                                encodedValue(value).
+                                appendWhitespace("\n");
                     }
 
                     public void propChanged(String nodePath, String propName, String oldValue, String newValue) {
                         buff.append("^ ").
-                            key(PathUtils.concat(nodePath, propName)).
-                            encodedValue(newValue).
-                            appendWhitespace("\n");
+                                key(PathUtils.concat(nodePath, propName)).
+                                encodedValue(newValue).
+                                appendWhitespace("\n");
                     }
 
                     public void propDeleted(String nodePath, String propName, String value) {
@@ -184,17 +184,17 @@ public class MicroKernelImpl implements 
                         // using the "- <path>" notation we're representing
                         // property deletions as "^ <path>:null"
                         buff.append("^ ").
-                            key(PathUtils.concat(nodePath, propName)).
-                            encodedValue("null").
-                            appendWhitespace("\n");
+                                key(PathUtils.concat(nodePath, propName)).
+                                encodedValue("null").
+                                appendWhitespace("\n");
                     }
 
                     public void childNodeAdded(String nodePath, String childName, String id) {
                         addedNodes.put(id, PathUtils.concat(nodePath, childName));
                         buff.append("+ ").
-                            key(PathUtils.concat(nodePath, childName)).object();
+                                key(PathUtils.concat(nodePath, childName)).object();
                         try {
-                            toJson(buff, rep.getStore().getNode(id), childName, Integer.MAX_VALUE, 0, -1);
+                            toJson(buff, rep.getPersistenceManager().getNode(id), childName, Integer.MAX_VALUE, 0, -1);
                         } catch (Exception e) {
                             buff.value("ERROR: failed to retrieve node " + id);
                         }
@@ -225,19 +225,19 @@ public class MicroKernelImpl implements 
                     buff.reset();
 
                     // TODO refactor code, avoid duplication
-                    NodeUtils.diff(path, node1, node2, true, rep.getStore(), new NodeDiffHandler() {
+                    NodeUtils.diff(path, node1, node2, true, rep.getPersistenceManager(), new NodeDiffHandler() {
                         public void propAdded(String nodePath, String propName, String value) {
                             buff.append("+ ").
-                                key(PathUtils.concat(nodePath, propName)).
-                                encodedValue(value).
-                                append("\n");
+                                    key(PathUtils.concat(nodePath, propName)).
+                                    encodedValue(value).
+                                    append("\n");
                         }
 
                         public void propChanged(String nodePath, String propName, String oldValue, String newValue) {
                             buff.append("^ ").
-                                key(PathUtils.concat(nodePath, propName)).
-                                encodedValue(newValue).
-                                append("\n");
+                                    key(PathUtils.concat(nodePath, propName)).
+                                    encodedValue(newValue).
+                                    append("\n");
                         }
 
                         public void propDeleted(String nodePath, String propName, String value) {
@@ -245,9 +245,9 @@ public class MicroKernelImpl implements 
                             // using the "- <path>" notation we're representing
                             // property deletions as "^ <path>:null"
                             buff.append("^ ").
-                                key(PathUtils.concat(nodePath, propName)).
-                                encodedValue("null").
-                                append("\n");
+                                    key(PathUtils.concat(nodePath, propName)).
+                                    encodedValue("null").
+                                    append("\n");
                         }
 
                         public void childNodeAdded(String nodePath, String childName, String id) {
@@ -256,9 +256,9 @@ public class MicroKernelImpl implements 
                                 return;
                             }
                             buff.append("+ ").
-                                key(PathUtils.concat(nodePath, childName)).object();
+                                    key(PathUtils.concat(nodePath, childName)).object();
                             try {
-                                toJson(buff, rep.getStore().getNode(id), childName, Integer.MAX_VALUE, 0, -1);
+                                toJson(buff, rep.getPersistenceManager().getNode(id), childName, Integer.MAX_VALUE, 0, -1);
                             } catch (Exception e) {
                                 buff.value("ERROR: failed to retrieve node " + id);
                             }
@@ -282,11 +282,11 @@ public class MicroKernelImpl implements 
                     // finally process moved nodes
                     for (Map.Entry<String, String> entry : addedNodes.entrySet()) {
                         buff.append("> ").
-                            // path/to/deleted/node
-                            key(removedNodes.get(entry.getKey())).
-                            // path/to/added/node
-                            value(entry.getValue()).
-                            append("\n");
+                                // path/to/deleted/node
+                                        key(removedNodes.get(entry.getKey())).
+                                // path/to/added/node
+                                        value(entry.getValue()).
+                                append("\n");
                     }
                 }
             } catch (Exception e) {
@@ -331,49 +331,49 @@ public class MicroKernelImpl implements 
                     break;
                 }
                 switch (r) {
-                case '+': {
-                    String relPath = t.readString();
-                    t.read(':');
-                    t.read('{');
-                    String nodePath = PathUtils.concat(path, relPath);
-                    String parentPath = PathUtils.getParentPath(nodePath);
-                    String nodeName = PathUtils.getName(nodePath);
-                    // build the list of added nodes recursively
-                    LinkedList<AddNodeOperation> list = new LinkedList<AddNodeOperation>();
-                    addNode(list, parentPath, nodeName, t);
-                    for (AddNodeOperation op : list) {
-                        cb.addNode(op.path, op.name, op.props);
+                    case '+': {
+                        String relPath = t.readString();
+                        t.read(':');
+                        t.read('{');
+                        String nodePath = PathUtils.concat(path, relPath);
+                        String parentPath = PathUtils.getParentPath(nodePath);
+                        String nodeName = PathUtils.getName(nodePath);
+                        // build the list of added nodes recursively
+                        LinkedList<AddNodeOperation> list = new LinkedList<AddNodeOperation>();
+                        addNode(list, parentPath, nodeName, t);
+                        for (AddNodeOperation op : list) {
+                            cb.addNode(op.path, op.name, op.props);
+                        }
+                        break;
                     }
-                    break;
-                }
-                case '-': {
-                    String relPath = t.readString();
-                    cb.removeNode(PathUtils.concat(path, relPath));
-                    break;
-                }
-                case '^': {
-                    String relPath = t.readString();
-                    t.read(':');
-                    String value;
-                    if (t.matches(JsopTokenizer.NULL)) {
-                        value = null;
-                    } else {
-                        value = t.readRawValue().trim();
+                    case '-': {
+                        String relPath = t.readString();
+                        cb.removeNode(PathUtils.concat(path, relPath));
+                        break;
                     }
-                    String nodePath = PathUtils.concat(path, relPath);
-                    String parentPath = PathUtils.getParentPath(nodePath);
-                    String propName = PathUtils.getName(nodePath);
-                    cb.setProperty(parentPath, propName, value);
-                    break;
-                }
-                case '>': {
-                    String relPath = t.readString();
-                    t.read(':');
-                    String targetPath = t.readString();
-                    if (! PathUtils.isAbsolute(targetPath)) {
-                        targetPath = PathUtils.concat(path, targetPath);
+                    case '^': {
+                        String relPath = t.readString();
+                        t.read(':');
+                        String value;
+                        if (t.matches(JsopTokenizer.NULL)) {
+                            value = null;
+                        } else {
+                            value = t.readRawValue().trim();
+                        }
+                        String nodePath = PathUtils.concat(path, relPath);
+                        String parentPath = PathUtils.getParentPath(nodePath);
+                        String propName = PathUtils.getName(nodePath);
+                        cb.setProperty(parentPath, propName, value);
+                        break;
                     }
-                    cb.moveNode(PathUtils.concat(path, relPath), targetPath);
+                    case '>': {
+                        String relPath = t.readString();
+                        t.read(':');
+                        String targetPath = t.readString();
+                        if (!PathUtils.isAbsolute(targetPath)) {
+                            targetPath = PathUtils.concat(path, targetPath);
+                        }
+                        cb.moveNode(PathUtils.concat(path, relPath), targetPath);
 /*
                     path = t.readString();
                     String from = PathUtils.concat(fromRoot, path);
@@ -432,10 +432,10 @@ public class MicroKernelImpl implements 
                         headRoot = headRoot.cloneAndRemoveChildNode(from, headRevId);
                     }
 */
-                    break;
-                }
-                default:
-                    throw new AssertionError("token type: " + t.getTokenType());
+                        break;
+                    }
+                    default:
+                        throw new AssertionError("token type: " + t.getTokenType());
                 }
             }
             String newHead = cb.doCommit();
@@ -451,7 +451,7 @@ public class MicroKernelImpl implements 
             throw new IllegalStateException("this instance has already been disposed");
         }
         try {
-            return rep.getStore().getBlobLength(blobId);
+            return rep.getPersistenceManager().getBlobLength(blobId);
         } catch (Exception e) {
             throw new MicroKernelException(e);
         }
@@ -462,7 +462,7 @@ public class MicroKernelImpl implements 
             throw new IllegalStateException("this instance has already been disposed");
         }
         try {
-            return rep.getStore().readBlob(blobId, pos, buff, off, length);
+            return rep.getPersistenceManager().readBlob(blobId, pos, buff, off, length);
         } catch (Exception e) {
             throw new MicroKernelException(e);
         }
@@ -473,7 +473,7 @@ public class MicroKernelImpl implements 
             throw new IllegalStateException("this instance has already been disposed");
         }
         try {
-            return rep.getStore().writeBlob(in);
+            return rep.getPersistenceManager().writeBlob(in);
         } catch (Exception e) {
             throw new MicroKernelException(e);
         }
@@ -497,7 +497,7 @@ public class MicroKernelImpl implements 
                     builder.key(childName).object();
                     if (depth > 0) {
                         String childId = child.getValue();
-                        toJson(builder, rep.getStore().getNode(childId), childName, depth - 1, 0, -1);
+                        toJson(builder, rep.getPersistenceManager().getNode(childId), childName, depth - 1, 0, -1);
                     }
                     builder.endObject();
                 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java Thu Aug 25 08:18:57 2011
@@ -16,10 +16,14 @@
  */
 package org.apache.jackrabbit.mk;
 
+import org.apache.jackrabbit.mk.blobs.BlobStore;
+import org.apache.jackrabbit.mk.blobs.FileBlobStore;
 import org.apache.jackrabbit.mk.store.Commit;
 import org.apache.jackrabbit.mk.store.CommitBuilder;
+import org.apache.jackrabbit.mk.store.H2RevisionStore;
 import org.apache.jackrabbit.mk.store.Node;
-import org.apache.jackrabbit.mk.store.ObjectStore;
+import org.apache.jackrabbit.mk.store.PersistenceManager;
+import org.apache.jackrabbit.mk.store.RevisionStore;
 import org.apache.jackrabbit.mk.util.PathUtils;
 
 import java.io.File;
@@ -32,11 +36,11 @@ public class Repository {
     final String homeDir;
     boolean initialized;
 
-    final ObjectStore store;
+    final PersistenceManager pm;
 
     public Repository(String homeDir) {
         this.homeDir = homeDir == null ? "." : homeDir;
-        store = new ObjectStore();
+        pm = new PersistenceManager();
         initialized = false;
     }
 
@@ -45,7 +49,20 @@ public class Repository {
             return;
         }
 
-        store.initialize(new File(homeDir));
+        File home = new File(homeDir);
+
+        RevisionStore revStore = new H2RevisionStore();
+        //RevisionStore revStore = new SimpleRevisionStore();
+        revStore.initialize(home);
+
+        BlobStore blobStore;
+        if (revStore instanceof BlobStore) {
+            blobStore = (BlobStore) revStore;
+        } else {
+            blobStore = new FileBlobStore(new File(home, "blobs").getCanonicalPath());
+        }
+
+        pm.initialize(revStore, blobStore);
 
         initialized = true;
     }
@@ -55,38 +72,38 @@ public class Repository {
             return;
         }
 
-        store.close();
+        pm.close();
 
         initialized = false;
     }
 
-    public ObjectStore getStore() {
+    public PersistenceManager getPersistenceManager() {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
 
-        return store;
+        return pm;
     }
 
     public String getHeadRevision() throws Exception {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
-        return store.getHeadCommit().getId();
+        return pm.getHeadCommit().getId();
     }
 
     public Commit getHeadCommit() throws Exception {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
-        return store.getHeadCommit();
+        return pm.getHeadCommit();
     }
 
     public Commit getCommit(String id) throws Exception {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
-        return store.getCommit(id);
+        return pm.getCommit(id);
     }
 
     public Node getNode(String revId, String path) throws Exception {
@@ -95,10 +112,10 @@ public class Repository {
         }
 
         String[] ids = resolvePath(revId, path);
-        return store.getNode(ids[ids.length - 1]);
+        return pm.getNode(ids[ids.length - 1]);
     }
 
-    public boolean nodeExists(String revId, String path)  {
+    public boolean nodeExists(String revId, String path) {
         if (!initialized) {
             throw new IllegalStateException("not initialized");
         }
@@ -109,13 +126,13 @@ public class Repository {
 
         try {
             String[] names = PathUtils.split(path);
-            Node parent = store.getNode(store.getCommit(revId).getRootNodeId());
+            Node parent = pm.getNode(pm.getCommit(revId).getRootNodeId());
             for (int i = 0; i < names.length; i++) {
                 String id = parent.getChildNodeEntries().get(names[i]);
                 if (id == null) {
                     return false;
                 }
-                parent = store.getNode(id);
+                parent = pm.getNode(id);
             }
             return true;
         } catch (Exception e) {
@@ -134,25 +151,25 @@ public class Repository {
             throw new IllegalArgumentException("illegal path");
         }
 
-        Commit commit = store.getCommit(revId);
+        Commit commit = pm.getCommit(revId);
 
         if (PathUtils.denotesRoot(path)) {
-            return new String[] { commit.getRootNodeId() };
+            return new String[]{commit.getRootNodeId()};
         }
         String[] names = PathUtils.split(path);
         String[] ids = new String[names.length + 1];
 
         // get root node
         ids[0] = commit.getRootNodeId();
-        Node parent = store.getNode(ids[0]);
-        // traverse path and store id of each element
+        Node parent = pm.getNode(ids[0]);
+        // traverse path and pm id of each element
         for (int i = 0; i < names.length; i++) {
             String id = parent.getChildNodeEntries().get(names[i]);
             if (id == null) {
                 throw new Exception("path not found: " + path);
             }
             ids[i + 1] = id;
-            parent = store.getNode(id);
+            parent = pm.getNode(id);
         }
         return ids;
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java Thu Aug 25 08:18:57 2011
@@ -29,10 +29,10 @@ import java.io.InputStream;
  * <li>predefined timeout (e.g. 50ms) for reading methods</li>
  * <li>portable to C</li>
  * <li>efficient support for large number of child nodes</li>
- * <li>integrated API for storing/retrieving large binaries (similar to DataStore API)</li>
+ * <li>integrated API for storing/retrieving large binaries (similar to existing DataStore API)</li>
  * <li>human-readable data serialization (e.g. JSON, YAML)</li>
  * </ul>
- *
+ * <p/>
  * The MicroKernel <b>Data Model</b>:
  * <ul>
  * <li>simple JSON-inspired data model: just nodes and properties</li>
@@ -47,45 +47,45 @@ import java.io.InputStream;
  * <li>properties and child nodes share the same namespace, i.e. a property and
  * a child node, sharing the same parent node, cannot have the same name</li>
  * </ul>
- *
+ * <p/>
  * <b>Architecture (overview)</b>:
  * <ol>
  * <li>JCR (full TCK-compliant implementation)</li>
  * <li>SPI (node types, workspaces, namespaces, access control, search, locking, ...)</li>
  * <li><i>MicroKernel</i></li>
  * </ol>
- *
+ * <p/>
  * <b>TBD</b>:
  * <ul>
  * <li>The concept of JCR workspaces is decorated by the SPI layer as top-level nodes
- *     in the MicroKernel (e.g. /default. /system); there's no need for
- *     inter-workspace operations, allows simplified versioning implementation</li>
+ * in the MicroKernel (e.g. /default. /system); there's no need for
+ * inter-workspace operations, allows simplified versioning implementation</li>
  * <li>should the MicroKernel support (polling) observation?
- *     => <code>getRevisions/getJournal</code> should be sufficient</li>
+ * => <code>getRevisions/getJournal</code> should be sufficient</li>
  * <li>nodes are primarily accessed by path. node identifiers (nodeId) are
- *     optional. if a new node is committed with a special marker property
- *     (e.g. <code>":id" : null</code>), a system-generated unique identifier will be
- *     assigned and exposed through the same <code>:id</code> property;
- *     the MicroKernel maintains an id<->path index</li>
+ * optional. if a new node is committed with a special marker property
+ * (e.g. <code>":id" : null</code>), a system-generated unique identifier will be
+ * assigned and exposed through the same <code>:id</code> property;
+ * the MicroKernel maintains an id<->path index</li>
  * <li>should the API provide methods for looking up id/path mappings
- *     (<code>getId/getPath</code>) or should every node with an assigned
- *     id expose a <code>:path</code> property (in addition to <code>:id</code>)?</li>
+ * (<code>getId/getPath</code>) or should every node with an assigned
+ * id expose a <code>:path</code> property (in addition to <code>:id</code>)?</li>
  * <li><code>jcr:uuid</code> is decorated by the SPI on top of MicroKernel;
- *     <code>jcr:uuid</code> is equivalent to the MicroKernel node identifier (<code>:id</code>)</li>
+ * <code>jcr:uuid</code> is equivalent to the MicroKernel node identifier (<code>:id</code>)</li>
  * <li>should the MicroKernel provide built-in support for shareable nodes?
- *     => no (tentative)</li>
+ * => no (tentative)</li>
  * </ul>
- *
+ * <p/>
  * <b>JSON/JSOP</b>:
  * <ul>
  * <li>do we need to specify a 'reorder' op in jsop? perhaps based on 'move'?
- *     => check full JSOP spec and davex</li>
+ * => check full JSOP spec and davex</li>
  * <li>do we need to specify a 'copy' op in jsop?
- *     => check full JSOP spec and davex</li>
+ * => check full JSOP spec and davex</li>
  * <li>how should an incomplete list of child nodes be represented in json?
- *     => length(array) != :childNodeCount</li>
+ * => length(array) != :childNodeCount</li>
  * <li>consider YAML as an alternative to the JSON representation =>
- *     supports ordered sequence of name:value pairs, richer set of data/collection types</li>
+ * supports ordered sequence of name:value pairs, richer set of data/collection types</li>
  * </ul>
  *
  * @todo check DavEx protocol for potential overlap/input (e.g. JSON format)
@@ -116,26 +116,25 @@ public interface MicroKernel {
      * [ { "id" : "<revisionId>", "ts" : <revisionTimestamp> }, ... ]
      * </pre>
      *
-     * @todo provide a per revision size hint? (TBD)
-     *
-     * @param since timestamp (ms) of earliest revision to be returned
+     * @param since      timestamp (ms) of earliest revision to be returned
      * @param maxEntries maximum #entries to be returned;
      *                   if < 0, no limit will be applied.
      * @return a chronological list of revisions in JSON format.
      * @throws MicroKernelException if an error occurs
+     * @todo provide a per revision size hint? (TBD)
      */
     String /* jsonArray */ getRevisions(long since, int maxEntries)
             throws MicroKernelException;
 
     /**
      * Wait for a commit to occur that is newer than the given revision number.
-     *
+     * <p/>
      * This method is useful efficient polling. The method will return the current head revision
      * if it is newer than the given old revision number, or wait until the given number of
      * milliseconds passed or a new head revision is available.
      *
      * @param maxWaitMillis the maximum number of milliseconds to wait (0 if the
-     *            method should not wait).
+     *                      method should not wait).
      * @return the current head revision
      * @throws MicroKernelException if an error occurs
      * @throws InterruptedException if the thread was interrupted
@@ -153,7 +152,7 @@ public interface MicroKernel {
      * </pre>
      *
      * @param fromRevisionId first revision to be returned in journal
-     * @param toRevisionId last revision to be returned in journal
+     * @param toRevisionId   last revision to be returned in journal
      * @return a chronological list of revisions in JSON format
      * @throws MicroKernelException if an error occurs
      */
@@ -166,7 +165,7 @@ public interface MicroKernel {
     /**
      * Determines whether the specified node exists.
      *
-     * @param path path denoting node
+     * @param path       path denoting node
      * @param revisionId revision
      * @return <code>true</code> if the specified node exists, otherwise <code>false</code>
      * @throws MicroKernelException if an error occurs
@@ -194,7 +193,7 @@ public interface MicroKernel {
      * </pre>
      * The collection of name/value pairs denoting child nodes is assumed to be
      * ordered.
-     *
+     * <p/>
      * Remarks:
      * <ul>
      * <li>If the property <code>:childNodeCount</code> is 0, then the
@@ -207,7 +206,7 @@ public interface MicroKernel {
      * This method is a convenience method for
      * <code>getNodes(path, revisionId, 1, 0, -1)</code>
      *
-     * @param path path denoting root of node tree to be retrieved
+     * @param path       path denoting root of node tree to be retrieved
      * @param revisionId revision
      * @return node tree in JSON format
      * @throws MicroKernelException if an error occurs
@@ -236,13 +235,13 @@ public interface MicroKernel {
      * </table>
      * Offset and count only affect the returned child node list of this node.
      *
-     * @param path path denoting root of node tree to be retrieved
+     * @param path       path denoting root of node tree to be retrieved
      * @param revisionId revision
-     * @param depth maximum depth of returned tree
-     * @param offset start position in child node list (0 to start at the
-     *            beginning)
-     * @param count maximum number of child nodes to retrieve (-1 for as many as
-     *            possible)
+     * @param depth      maximum depth of returned tree
+     * @param offset     start position in child node list (0 to start at the
+     *                   beginning)
+     * @param count      maximum number of child nodes to retrieve (-1 for as many as
+     *                   possible)
      * @return node tree in JSON format
      * @throws MicroKernelException if an error occurs
      */
@@ -253,10 +252,10 @@ public interface MicroKernel {
     /**
      * Applies the specified changes on the specified target node.
      *
-     * @param path path denoting target node
-     * @param jsonDiff changes to be applied in JSON diff format.
+     * @param path       path denoting target node
+     * @param jsonDiff   changes to be applied in JSON diff format.
      * @param revisionId revision the changes are based on
-     * @param message commit message
+     * @param message    commit message
      * @return id of newly created revision
      * @throws MicroKernelException if an error occurs
      */
@@ -269,11 +268,10 @@ public interface MicroKernel {
     /**
      * Returns the length of the specified blob.
      *
-     * @todo encode length in id?
-     *
      * @param blobId blob identifier
      * @return length of the specified blob
      * @throws MicroKernelException if an error occurs
+     * @todo encode length in id?
      */
     long getLength(String blobId) throws MicroKernelException;
 
@@ -284,10 +282,10 @@ public interface MicroKernel {
      * The number of bytes actually read is returned as an integer.
      *
      * @param blobId blob identifier
-     * @param pos the offset within the blob
-     * @param buff the buffer into which the data is read.
-     * @param off the start offset in array <code>buff</code>
-     *            at which the data is written.
+     * @param pos    the offset within the blob
+     * @param buff   the buffer into which the data is read.
+     * @param off    the start offset in array <code>buff</code>
+     *               at which the data is written.
      * @param length the maximum number of bytes to read
      * @return the total number of bytes read into the buffer, or
      *         <code>-1</code> if there is no more data because the end of
@@ -303,7 +301,7 @@ public interface MicroKernel {
      * <p/>
      * If identical stream content has been stored previously, then the existing
      * identifier will be returned instead of storing a redundant copy.
-
+     *
      * @param in InputStream providing the blob content
      * @return blob identifier associated with the given content
      * @throws MicroKernelException if an error occurs

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java?rev=1161434&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java Thu Aug 25 08:18:57 2011
@@ -0,0 +1,235 @@
+/*
+ * 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.blobs;
+
+import org.apache.jackrabbit.mk.util.IOUtils;
+import org.apache.jackrabbit.mk.util.StringUtils;
+import org.h2.util.SmallLRUCache;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+
+/**
+ * An abstract data store that splits the binaries in relatively small blocks.
+ * Each data store id a list of zero or more entries. Each entry is either
+ * <ul>
+ * <li>data (a number of bytes), or</li>
+ * <li>the hash code of the content of a number of bytes, or</li>
+ * <li>the hash code of the content of a data store id (indirect hash)</li>
+ * </ul>
+ * Thanks to the indirection, blocks can be kept relatively small, so that
+ * caching is simpler, and so that the storage backend doesn't need to support
+ * arbitrary size blobs (some storage backends buffer blobs in memory) and fast
+ * seeks (some storage backends re-read the whole blob when seeking).
+ * <p/>
+ * The the format of a 'data' entry is: type (one byte; 0 for data), length
+ * (variable size int), data (bytes).
+ * <p/>
+ * The format of a 'hash of content' entry is: type (one byte; 1 for hash),
+ * level (variable size int, 0 meaning not nested), size (variable size long),
+ * hash code length (variable size int), hash code.
+ * <p/>
+ * The format of a 'hash of data store id' entry is: type (one byte; 1 for
+ * hash), level (variable size int, nesting level), total size (variable size
+ * long), size of data store id (variable size long), hash code length (variable
+ * size int), hash code.
+ */
+public abstract class AbstractBlobStore implements BlobStore {
+
+    protected static final int TYPE_DATA = 0;
+    protected static final int TYPE_HASH = 1;
+    protected static final int TYPE_HASH_COMPRESSED = 2;
+
+    /**
+     * The minimum size of a block. Smaller blocks are stored (the data store id
+     * is the data itself).
+     */
+    private int blockSizeMin = 256;
+
+    /**
+     * The size of a block. This number has been found to be as fast as larger
+     * values, and faster than smaller values.
+     */
+    private int blockSize = 128 * 1024;
+
+    private static final String HASH_ALGORITHM = "SHA-1";
+
+    public void setBlockSizeMin(int x) {
+        this.blockSizeMin = x;
+    }
+
+    public void setBlockSize(int x) {
+        this.blockSize = x;
+    }
+
+    private SmallLRUCache<String, byte[]> cache = SmallLRUCache.newInstance(5);
+
+    public String writeBlob(InputStream in) throws Exception {
+        ByteArrayOutputStream idStream = new ByteArrayOutputStream();
+        convertBlobToId(in, idStream, 0, 0);
+        byte[] id = idStream.toByteArray();
+        // System.out.println("    write blob " +  StringUtils.convertBytesToHex(id));
+        return StringUtils.convertBytesToHex(id);
+    }
+
+    private void convertBlobToId(InputStream in, ByteArrayOutputStream idStream, int level, long totalLength) throws Exception {
+        byte[] block = new byte[blockSize];
+        int count = 0;
+        while (true) {
+            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
+            ByteArrayOutputStream buff = new ByteArrayOutputStream();
+            DigestOutputStream dout = new DigestOutputStream(buff, digest);
+            int blockLen = IOUtils.readFully(in, block, 0, block.length);
+            count++;
+            if (blockLen == 0) {
+                break;
+            } else if (blockLen < blockSizeMin) {
+                idStream.write(TYPE_DATA);
+                IOUtils.writeVarInt(idStream, blockLen);
+                idStream.write(block, 0, blockLen);
+                totalLength += blockLen;
+            } else {
+                dout.write(block, 0, blockLen);
+                byte[] blockId = digest.digest();
+                idStream.write(TYPE_HASH);
+                IOUtils.writeVarInt(idStream, level);
+                if (level > 0) {
+                    IOUtils.writeVarLong(idStream, totalLength);
+                }
+                IOUtils.writeVarLong(idStream, blockLen);
+                totalLength += blockLen;
+                IOUtils.writeVarInt(idStream, blockId.length);
+                idStream.write(blockId);
+                byte[] data = buff.toByteArray();
+                storeBlock(blockId, level, data);
+            }
+            if (idStream.size() > blockSize / 2) {
+                // convert large ids to a block, but ensure it can be stored as
+                // one block (otherwise the indirection no longer works)
+                byte[] idBlock = idStream.toByteArray();
+                idStream.reset();
+                convertBlobToId(new ByteArrayInputStream(idBlock), idStream, level + 1, totalLength);
+                count = 1;
+            }
+        }
+        if (count > 0 && idStream.size() > blockSizeMin) {
+            // at the very end, convert large ids to a block,
+            // because large block ids are not handy
+            // (specially if they are used to read data in small chunks)
+            byte[] idBlock = idStream.toByteArray();
+            idStream.reset();
+            convertBlobToId(new ByteArrayInputStream(idBlock), idStream, level + 1, totalLength);
+        }
+    }
+
+    protected abstract void storeBlock(byte[] blockId, int level, byte[] data) throws Exception;
+
+    public int readBlob(String blobId, long pos, byte[] buff, int off, int length) throws Exception {
+        byte[] id = StringUtils.convertHexToBytes(blobId);
+        ByteArrayInputStream idStream = new ByteArrayInputStream(id);
+        while (true) {
+            int type = idStream.read();
+            if (type == -1) {
+                return -1;
+            } else if (type == TYPE_DATA) {
+                int len = IOUtils.readVarInt(idStream);
+                if (pos < len) {
+                    IOUtils.skipFully(idStream, (int) pos);
+                    len -= pos;
+                    if (length < len) {
+                        len = length;
+                    }
+                    IOUtils.readFully(idStream, buff, off, len);
+                    return len;
+                }
+                IOUtils.skipFully(idStream, len);
+                pos -= len;
+            } else if (type == TYPE_HASH) {
+                int level = IOUtils.readVarInt(idStream);
+                long totalLength = IOUtils.readVarLong(idStream);
+                if (level > 0) {
+                    // block length (ignored)
+                    IOUtils.readVarLong(idStream);
+                }
+                byte[] digest = new byte[IOUtils.readVarInt(idStream)];
+                IOUtils.readFully(idStream, digest, 0, digest.length);
+                if (pos >= totalLength) {
+                    pos -= totalLength;
+                } else {
+                    byte[] block = readBlock(digest);
+                    if (level > 0) {
+                        idStream = new ByteArrayInputStream(block);
+                    } else {
+                        ByteArrayInputStream in = new ByteArrayInputStream(block);
+                        IOUtils.skipFully(in, (int) pos);
+                        return IOUtils.readFully(in, buff, off, length);
+                    }
+                }
+            } else {
+                throw new IOException("Unknown blobs id type " + type + " for blob " + blobId);
+            }
+        }
+    }
+
+    private byte[] readBlock(byte[] digest) throws Exception {
+        String id = StringUtils.convertBytesToHex(digest);
+        byte[] block = cache.get(id);
+        if (block == null) {
+            block = readBlockFromBackend(digest);
+            cache.put(id, block);
+        }
+        return block;
+    }
+
+    protected abstract byte[] readBlockFromBackend(byte[] blockId) throws Exception;
+
+    public long getBlobLength(String blobId) throws IOException {
+        byte[] id = StringUtils.convertHexToBytes(blobId);
+        ByteArrayInputStream idStream = new ByteArrayInputStream(id);
+        long totalLength = 0;
+        while (true) {
+            int type = idStream.read();
+            if (type == -1) {
+                break;
+            }
+            if (type == TYPE_DATA) {
+                int len = IOUtils.readVarInt(idStream);
+                IOUtils.skipFully(idStream, len);
+                totalLength += len;
+            } else if (type == TYPE_HASH) {
+                int level = IOUtils.readVarInt(idStream);
+                totalLength += IOUtils.readVarLong(idStream);
+                if (level > 0) {
+                    // block length (ignored)
+                    IOUtils.readVarLong(idStream);
+                }
+                int digestLength = IOUtils.readVarInt(idStream);
+                IOUtils.skipFully(idStream, digestLength);
+            } else {
+                throw new IOException("Datastore id type " + type + " for blob " + blobId);
+            }
+        }
+        return totalLength;
+    }
+
+    public abstract void clear();
+
+}

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java?rev=1161434&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java Thu Aug 25 08:18:57 2011
@@ -0,0 +1,73 @@
+/*
+ * 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.blobs;
+
+import org.apache.jackrabbit.mk.util.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An input stream to simplify reading from a store.
+ * See also MicroKernelInputStream.
+ */
+public class BlobStoreInputStream extends InputStream {
+
+    private final AbstractBlobStore store;
+    private final String id;
+    private long pos;
+    private byte[] oneByteBuff;
+
+    public BlobStoreInputStream(AbstractBlobStore store, String id) {
+        this.store = store;
+        this.id = id;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        int l;
+        try {
+            l = store.readBlob(id, pos, b, off, len);
+        } catch (Exception e) {
+            throw new IOException(e);
+        }
+        if (l < 0) {
+            return l;
+        }
+        pos += l;
+        return l;
+    }
+
+    public int read() throws IOException {
+        if (oneByteBuff == null) {
+            oneByteBuff = new byte[1];
+        }
+        int len = read(oneByteBuff, 0, 1);
+        if (len < 0) {
+            return len;
+        }
+        return oneByteBuff[0] & 0xff;
+    }
+
+    public static byte[] readFully(AbstractBlobStore store, String id) throws IOException {
+        int len = (int) store.getBlobLength(id);
+        byte[] buff = new byte[len];
+        BlobStoreInputStream in = new BlobStoreInputStream(store, id);
+        IOUtils.readFully(in, buff, 0, len);
+        return buff;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java?rev=1161434&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java Thu Aug 25 08:18:57 2011
@@ -0,0 +1,126 @@
+/*
+ * 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.blobs;
+
+import org.apache.jackrabbit.mk.util.StringUtils;
+import org.h2.jdbcx.JdbcConnectionPool;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * A database blob store.
+ */
+public class DbBlobStore extends AbstractBlobStore {
+
+    private JdbcConnectionPool cp;
+
+    public void setConnectionPool(JdbcConnectionPool cp) throws SQLException {
+        this.cp = cp;
+        Connection conn = cp.getConnection();
+        Statement stat = conn.createStatement();
+        stat.execute("create table if not exists datastore_meta" +
+                "(id varchar primary key, level int, lastMod bigint)");
+        stat.execute("create table if not exists datastore_data" +
+                "(id varchar primary key, data binary)");
+        conn.close();
+    }
+
+    @Override
+    protected void storeBlock(byte[] blockId, int level, byte[] data) throws SQLException {
+        Connection conn = cp.getConnection();
+        try {
+            String id = StringUtils.convertBytesToHex(blockId);
+            long now = System.currentTimeMillis();
+            PreparedStatement prep = conn.prepareStatement(
+                    "update datastore_meta set lastMod = ? where id = ?");
+            int count;
+            try {
+                prep.setLong(1, now);
+                prep.setString(2, id);
+                count = prep.executeUpdate();
+            } finally {
+                prep.close();
+            }
+            if (count == 0) {
+                try {
+                    prep = conn.prepareStatement(
+                            "insert into datastore_meta(id, level, lastMod) values(?, ?, ?)");
+                    try {
+                        prep.setString(1, id);
+                        prep.setInt(2, level);
+                        prep.setLong(3, now);
+                        prep.execute();
+                    } finally {
+                        prep.close();
+                    }
+                } catch (SQLException e) {
+                    // already exists - ok
+                }
+                try {
+                    prep = conn.prepareStatement(
+                            "insert into datastore_data(id, data) values(?, ?)");
+                    try {
+                        prep.setString(1, id);
+                        prep.setBytes(2, data);
+                        prep.execute();
+                    } finally {
+                        prep.close();
+                    }
+                } catch (SQLException e) {
+                    // already exists - ok
+                }
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Override
+    protected byte[] readBlockFromBackend(byte[] blockId) throws Exception {
+        Connection conn = cp.getConnection();
+        try {
+            PreparedStatement prep = conn.prepareStatement(
+                    "select data from datastore_data where id = ?");
+            try {
+                String id = StringUtils.convertBytesToHex(blockId);
+                prep.setString(1, id);
+                ResultSet rs = prep.executeQuery();
+                if (!rs.next()) {
+                    throw new IOException("Datastore block " + id + " not found");
+                }
+                byte[] data = rs.getBytes(1);
+                // System.out.println("    read block " + id + " blockLen: " + data.length + " [0]: " + data[0]);
+                return data;
+            } finally {
+                prep.close();
+            }
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Override
+    public void clear() {
+        // TODO currently not cleared
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java?rev=1161434&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java Thu Aug 25 08:18:57 2011
@@ -0,0 +1,82 @@
+/*
+ * 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.blobs;
+
+import org.apache.jackrabbit.mk.util.StringUtils;
+import org.h2.util.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * A file blob store.
+ */
+public class FileBlobStore extends AbstractBlobStore {
+
+    final private File baseDir;
+
+    public FileBlobStore(String dir) {
+        baseDir = new File(dir);
+        baseDir.mkdirs();
+    }
+
+    @Override
+    protected void storeBlock(byte[] blockId, int level, byte[] data) throws IOException {
+        File f = getFile(blockId);
+        if (f.exists()) {
+            return;
+        }
+        File parent = f.getParentFile();
+        if (!parent.exists()) {
+            parent.mkdirs();
+        }
+        File temp = new File(parent, f.getName() + ".temp");
+        FileOutputStream out = new FileOutputStream(temp);
+        out.write(data);
+        out.close();
+        temp.renameTo(f);
+    }
+
+    private File getFile(byte[] blockId) {
+        String id = StringUtils.convertBytesToHex(blockId) + ".dat";
+        File dir = new File(baseDir, id.substring(0, 2));
+        File f = new File(dir, id);
+        return f;
+    }
+
+    @Override
+    protected byte[] readBlockFromBackend(byte[] blockId) throws IOException {
+        File f = getFile(blockId);
+        int length = (int) f.length();
+        byte[] data = new byte[length];
+        FileInputStream in = new FileInputStream(f);
+        try {
+            IOUtils.readFully(in, data, 0, length);
+        } finally {
+            in.close();
+        }
+        return data;
+    }
+
+    @Override
+    public void clear() {
+        // TODO currently not cleared
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java?rev=1161434&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java Thu Aug 25 08:18:57 2011
@@ -0,0 +1,49 @@
+/*
+ * 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.blobs;
+
+import org.apache.jackrabbit.mk.util.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * A memory blob store. Useful for testing.
+ */
+public class MemoryBlobStore extends AbstractBlobStore {
+
+    private HashMap<String, byte[]> map = new HashMap<String, byte[]>();
+
+    @Override
+    protected byte[] readBlockFromBackend(byte[] blockId) {
+        return map.get(getId(blockId));
+    }
+
+    private String getId(byte[] blockId) {
+        return StringUtils.convertBytesToHex(blockId);
+    }
+
+    @Override
+    protected void storeBlock(byte[] blockId, int level, byte[] data) {
+        map.put(getId(blockId), data);
+    }
+
+    @Override
+    public void clear() {
+        map.clear();
+    }
+
+}

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=1161434&r1=1161433&r2=1161434&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 Thu Aug 25 08:18:57 2011
@@ -146,8 +146,7 @@ public final class JsonBuilder {
         private void optionalComma() throws IOException {
             if (hasKeys) {
                 writer.append(',');
-            }
-            else {
+            } else {
                 hasKeys = true;
             }
         }
@@ -233,8 +232,7 @@ public final class JsonBuilder {
         private void optionalComma() throws IOException {
             if (hasValues) {
                 writer.append(',');
-            }
-            else {
+            } else {
                 hasValues = true;
             }
         }
@@ -244,14 +242,14 @@ public final class JsonBuilder {
      * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F).
      */
     public static String escape(String string) {
-        if(string == null) {
+        if (string == null) {
             return null;
         }
 
         StringBuilder sb = new StringBuilder();
-        for(int i = 0; i < string.length(); i++) {
+        for (int i = 0; i < string.length(); i++) {
             char ch = string.charAt(i);
-            switch(ch) {
+            switch (ch) {
                 case '"':
                     sb.append("\\\"");
                     break;
@@ -285,8 +283,7 @@ public final class JsonBuilder {
                             sb.append('0');
                         }
                         sb.append(ss.toUpperCase());
-                    }
-                    else {
+                    } else {
                         sb.append(ch);
                     }
             }
@@ -314,15 +311,15 @@ 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);
+                ? "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);
+                ? "null"
+                : Double.toString(value);
     }
 
     public static String encode(Number value) {

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java Thu Aug 25 08:18:57 2011
@@ -215,63 +215,63 @@ public class JsopBuilder {
     /**
      * Escape a string into the target buffer.
      *
-     * @param s the string to escape
+     * @param s      the string to escape
      * @param length the number of characters.
-     * @param buff the target buffer
+     * @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');
+                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);
                     }
-                    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);
-                }
             }
         }
     }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java?rev=1161434&r1=1161433&r2=1161434&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java Thu Aug 25 08:18:57 2011
@@ -25,7 +25,7 @@ public class JsopTokenizer {
     public final static int COMMENT = 5, ERROR = 6, END = 7;
 
     private static final String[] TYPE = {
-        "string", "number", "true", "false", "null", "error", "end"
+            "string", "number", "true", "false", "null", "error", "end"
     };
 
     private final String jsop;
@@ -153,63 +153,54 @@ public class JsopTokenizer {
         }
         int start = pos++;
         switch (ch) {
-        case '\"': {
-            while (true) {
-                ch = jsop.charAt(pos++);
-                if (ch == '\"') {
-                    break;
-                } else if (ch == '\\') {
-                    currentEscaped = true;
-                    pos++;
+            case '\"': {
+                while (true) {
+                    ch = jsop.charAt(pos++);
+                    if (ch == '\"') {
+                        break;
+                    } else if (ch == '\\') {
+                        currentEscaped = true;
+                        pos++;
+                    }
                 }
+                currentToken = jsop.substring(start + 1, pos - 1);
+                return STRING;
             }
-            currentToken = jsop.substring(start + 1, pos - 1);
-            return STRING;
-        }
-        case '{':
-        case '}':
-        case '[':
-        case ']':
-        case '+':
-        case ':':
-        case ',':
-        case '>':
-        case '^':
-            return ch;
-        case '/': {
-            ch = jsop.charAt(pos);
-            if (ch != '*') {
-                return '/';
-            }
-            pos++;
-            while (true) {
-                ch = jsop.charAt(pos++);
-                if (ch == '*' && jsop.charAt(pos) == '/') {
-                    break;
+            case '{':
+            case '}':
+            case '[':
+            case ']':
+            case '+':
+            case ':':
+            case ',':
+            case '>':
+            case '^':
+                return ch;
+            case '/': {
+                ch = jsop.charAt(pos);
+                if (ch != '*') {
+                    return '/';
                 }
-            }
-            currentToken = jsop.substring(start + 2, pos - 1);
-            pos += 2;
-            return COMMENT;
-        }
-        case '-':
-            ch = jsop.charAt(pos);
-            if (ch < '0' || ch > '9') {
-                // lookahead
-                return '-';
-            }
-            // else fall though
-        default:
-            if (ch >= '0' && ch <= '9') {
-                while (pos < length) {
-                    ch = jsop.charAt(pos);
-                    if (ch < '0' || ch > '9') {
+                pos++;
+                while (true) {
+                    ch = jsop.charAt(pos++);
+                    if (ch == '*' && jsop.charAt(pos) == '/') {
                         break;
                     }
-                    pos++;
                 }
-                if (ch == '.') {
-                    pos++;
+                currentToken = jsop.substring(start + 2, pos - 1);
+                pos += 2;
+                return COMMENT;
+            }
+            case '-':
+                ch = jsop.charAt(pos);
+                if (ch < '0' || ch > '9') {
+                    // lookahead
+                    return '-';
+                }
+                // else fall though
+            default:
+                if (ch >= '0' && ch <= '9') {
                     while (pos < length) {
                         ch = jsop.charAt(pos);
                         if (ch < '0' || ch > '9') {
@@ -217,44 +208,53 @@ public class JsopTokenizer {
                         }
                         pos++;
                     }
-                }
-                if (ch == 'e' || ch == 'E') {
-                    ch = jsop.charAt(++pos);
-                    if (ch == '+' || ch == '-') {
+                    if (ch == '.') {
+                        pos++;
+                        while (pos < length) {
+                            ch = jsop.charAt(pos);
+                            if (ch < '0' || ch > '9') {
+                                break;
+                            }
+                            pos++;
+                        }
+                    }
+                    if (ch == 'e' || ch == 'E') {
                         ch = jsop.charAt(++pos);
+                        if (ch == '+' || ch == '-') {
+                            ch = jsop.charAt(++pos);
+                        }
+                        while (pos < length) {
+                            ch = jsop.charAt(pos);
+                            if (ch < '0' || ch > '9') {
+                                break;
+                            }
+                            pos++;
+                        }
                     }
+                    currentToken = jsop.substring(start, pos);
+                    return NUMBER;
+                } else if (ch >= 'a' && ch <= 'z') {
                     while (pos < length) {
                         ch = jsop.charAt(pos);
-                        if (ch < '0' || ch > '9') {
+                        if (ch < 'a' || ch > 'z') {
                             break;
                         }
                         pos++;
                     }
-                }
-                currentToken = jsop.substring(start, pos);
-                return NUMBER;
-            } else if (ch >= 'a' && ch <= 'z') {
-                while (pos < length) {
-                    ch = jsop.charAt(pos);
-                    if (ch < 'a' || ch > 'z') {
-                        break;
+                    String s = jsop.substring(start, pos);
+                    if ("null".equals(s)) {
+                        return NULL;
+                    } else if ("true".equals(s)) {
+                        return TRUE;
+                    } else if ("false".equals(s)) {
+                        return FALSE;
+                    } else {
+                        // currentToken = s;
+                        // return STRING;
+                        throw getFormatException(jsop, pos);
                     }
-                    pos++;
-                }
-                String s = jsop.substring(start, pos);
-                if ("null".equals(s)) {
-                    return NULL;
-                } else if ("true".equals(s)) {
-                    return TRUE;
-                } else if ("false".equals(s)) {
-                    return FALSE;
-                } else {
-                    // currentToken = s;
-                    // return STRING;
-                    throw getFormatException(jsop, pos);
                 }
-            }
-            throw getFormatException(jsop, pos);
+                throw getFormatException(jsop, pos);
         }
     }
 
@@ -275,42 +275,42 @@ public class JsopTokenizer {
                 }
                 c = s.charAt(++i);
                 switch (c) {
-                case '"':
-                    buff.append('"');
-                    break;
-                case '\\':
-                    buff.append('\\');
-                    break;
-                case '/':
-                    buff.append('/');
-                    break;
-                case 'b':
-                    buff.append('\b');
-                    break;
-                case 'f':
-                    buff.append('\f');
-                    break;
-                case 'n':
-                    buff.append('\n');
-                    break;
-                case 'r':
-                    buff.append('\r');
-                    break;
-                case 't':
-                    buff.append('\t');
-                    break;
-                case 'u': {
-                    try {
-                        c = (char) (Integer.parseInt(s.substring(i + 1, i + 5), 16));
-                    } catch (NumberFormatException e) {
-                        throw getFormatException(s, i);
+                    case '"':
+                        buff.append('"');
+                        break;
+                    case '\\':
+                        buff.append('\\');
+                        break;
+                    case '/':
+                        buff.append('/');
+                        break;
+                    case 'b':
+                        buff.append('\b');
+                        break;
+                    case 'f':
+                        buff.append('\f');
+                        break;
+                    case 'n':
+                        buff.append('\n');
+                        break;
+                    case 'r':
+                        buff.append('\r');
+                        break;
+                    case 't':
+                        buff.append('\t');
+                        break;
+                    case 'u': {
+                        try {
+                            c = (char) (Integer.parseInt(s.substring(i + 1, i + 5), 16));
+                        } catch (NumberFormatException e) {
+                            throw getFormatException(s, i);
+                        }
+                        i += 4;
+                        buff.append(c);
+                        break;
                     }
-                    i += 4;
-                    buff.append(c);
-                    break;
-                }
-                default:
-                    throw getFormatException(s, i);
+                    default:
+                        throw getFormatException(s, i);
                 }
             } else {
                 buff.append(c);
@@ -335,7 +335,7 @@ public class JsopTokenizer {
      * Add an asterisk ('[*]') at the given position. This format is used to
      * show where parsing failed in a statement.
      *
-     * @param s the text
+     * @param s     the text
      * @param index the position
      * @return the text with asterisk
      */
@@ -360,36 +360,36 @@ public class JsopTokenizer {
 
     private void skipRawValue() {
         switch (currentType) {
-        case '[':
-            read();
-            if (!matches(']')) {
-                do {
-                    skipRawValue();
-                } while (matches(','));
-                read(']');
-            }
-            break;
-        case '{':
-            read();
-            if (!matches('}')) {
-                do {
-                    readString();
-                    read(':');
-                    skipRawValue();
-                } while (matches(','));
-                read('}');
-            }
-            break;
-        case NULL:
-        case NUMBER:
-        case TRUE:
-        case FALSE:
-        case COMMENT:
-        case STRING:
-            read();
-            break;
-        default:
-            throw getFormatException(jsop, pos, "value");
+            case '[':
+                read();
+                if (!matches(']')) {
+                    do {
+                        skipRawValue();
+                    } while (matches(','));
+                    read(']');
+                }
+                break;
+            case '{':
+                read();
+                if (!matches('}')) {
+                    do {
+                        readString();
+                        read(':');
+                        skipRawValue();
+                    } while (matches(','));
+                    read('}');
+                }
+                break;
+            case NULL:
+            case NUMBER:
+            case TRUE:
+            case FALSE:
+            case COMMENT:
+            case STRING:
+                read();
+                break;
+            default:
+                throw getFormatException(jsop, pos, "value");
         }
     }
 



Mime
View raw message