Index: src/test/java/org/apache/jackrabbit/value/GenericValueFactoryTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/value/GenericValueFactoryTest.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/value/GenericValueFactoryTest.java	(revision 0)
@@ -0,0 +1,460 @@
+/*
+ * 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.value;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
+
+
+import junit.framework.TestCase;
+
+public class GenericValueFactoryTest extends TestCase {
+
+    private SimpleNamespaceResolver resolver;
+
+    private ValueFactory factory;
+
+    protected void setUp() {
+        resolver = new SimpleNamespaceResolver();
+        factory = new GenericValueFactory(resolver);
+        // factory = ValueFactoryImpl.getInstance();
+    }
+
+    /**
+     * Test boolean values.
+     */
+    public void testBooleanValues() {
+        try {
+            assertEquals(true, factory.createValue(true).getBoolean());
+            assertEquals(false, factory.createValue(false).getBoolean());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test date values.
+     */
+    public void testDateValues() {
+        try {
+            Calendar date = Calendar.getInstance();
+            assertEquals(
+                    date.getTimeInMillis(),
+                    factory.createValue(date).getDate().getTimeInMillis());
+            date.setTimeInMillis(1234567890);
+            assertEquals(
+                    date.getTimeInMillis(),
+                    factory.createValue(date).getDate().getTimeInMillis());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test double values.
+     */
+    public void testDoubleValues() {
+        try {
+            assertEquals(
+                    1.0, factory.createValue(1.0).getDouble(), 0);
+            assertEquals(
+                    Math.PI, factory.createValue(Math.PI).getDouble(), 0);
+            assertEquals(
+                    Double.MIN_VALUE,
+                    factory.createValue(Double.MIN_VALUE).getDouble(),
+                    0.0);
+            assertEquals(
+                    Double.MAX_VALUE,
+                    factory.createValue(Double.MAX_VALUE).getDouble(),
+                    0.0);
+            assertEquals(
+                    Double.NEGATIVE_INFINITY,
+                    factory.createValue(Double.NEGATIVE_INFINITY).getDouble(),
+                    0.0);
+            assertEquals(
+                    Double.POSITIVE_INFINITY,
+                    factory.createValue(Double.POSITIVE_INFINITY).getDouble(),
+                    0.0);
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test long values.
+     */
+    public void testLongValues() {
+        try {
+            assertEquals(0, factory.createValue(0).getLong());
+            assertEquals(1, factory.createValue(1).getLong());
+            assertEquals(
+                    Long.MIN_VALUE,
+                    factory.createValue(Long.MIN_VALUE).getLong());
+            assertEquals(
+                    Long.MAX_VALUE,
+                    factory.createValue(Long.MAX_VALUE).getLong());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test binary values.
+     */
+    public void testBinaryValues() {
+        try {
+            InputStream stream;
+            stream = new ByteArrayInputStream("".getBytes());
+            assertEquals("", factory.createValue(stream).getString());
+            stream = new ByteArrayInputStream("foo".getBytes());
+            assertEquals("foo", factory.createValue(stream).getString());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test string values.
+     */
+    public void testStringValues() {
+        try {
+            assertEquals("", factory.createValue("").getString());
+            assertEquals("foo", factory.createValue("foo").getString());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    private void assertInvalidName(String name) {
+        try {
+            factory.createValue(name, PropertyType.NAME);
+            fail("Invalid name: " + name);
+        } catch (ValueFormatException e) {
+        }
+    }
+
+    /**
+     * Test name values.
+     */
+    public void testNameValues() {
+        try {
+            resolver.setNamespace("foo", "foo-namespace");
+            assertEquals("foo", factory.createValue("foo", PropertyType.NAME).getString());
+            assertEquals("foo:baz", factory.createValue("foo:baz", PropertyType.NAME).getString());
+            Value value = factory.createValue("foo:baz", PropertyType.NAME);
+            resolver.setNamespace("bar", "foo-namespace");
+            assertEquals("bar:baz", value.getString());
+
+            assertInvalidName("");
+            assertInvalidName(".");
+            assertInvalidName("..");
+            assertInvalidName(" foo");
+            assertInvalidName("foo ");
+            assertInvalidName(":");
+            assertInvalidName(":foo");
+            assertInvalidName("foo:");
+            assertInvalidName("foo/bar");
+            assertInvalidName("foo[1]");
+            assertInvalidName("unknown:foo");
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    private void assertInvalidPath(String path) {
+        try {
+            factory.createValue(path, PropertyType.PATH);
+            fail("Invalid path: " + path);
+        } catch (ValueFormatException e) {
+        }
+    }
+
+    /**
+     * Test path values.
+     */
+    public void testPathValues() {
+        try {
+            resolver.setNamespace("foo", "foo-namespace");
+            assertEquals("/", factory.createValue("/", PropertyType.PATH).getString());
+            assertEquals("foo", factory.createValue("foo", PropertyType.PATH).getString());
+            assertEquals("/foo", factory.createValue("/foo", PropertyType.PATH).getString());
+            assertEquals("/foo/bar", factory.createValue("/foo/bar", PropertyType.PATH).getString());
+            assertEquals("foo:baz", factory.createValue("foo:baz", PropertyType.PATH).getString());
+            Value value = factory.createValue("/foo/./foo:baz/..", PropertyType.PATH);
+            resolver.setNamespace("bar", "foo-namespace");
+            assertEquals("/foo/./bar:baz/..", value.getString());
+
+            assertInvalidPath("");
+            assertInvalidPath(" foo");
+            assertInvalidPath("foo ");
+            assertInvalidPath(":");
+            assertInvalidPath(":foo");
+            assertInvalidPath("foo:");
+            assertInvalidPath("foo/");
+            assertInvalidPath("foo[1]");
+            assertInvalidPath("unknown:foo");
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+    /**
+     * Test boolean conversions.
+     */
+    public void testBooleanConversions() {
+        try {
+            assertEquals(
+                    Boolean.TRUE.toString(),
+                    factory.createValue(true).getString());
+            assertEquals(
+                    Boolean.FALSE.toString(),
+                    factory.createValue(false).getString());
+
+            try {
+                factory.createValue(true).getDate();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+
+            try {
+                factory.createValue(true).getDouble();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+
+            try {
+                factory.createValue(true).getLong();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+
+    /**
+     * Test double conversions.
+     */
+    public void testDoubleConversions() {
+        try {
+            assertEquals("1.0", factory.createValue(1.0).getString());
+            assertEquals(1, factory.createValue(1.0).getLong());
+            assertEquals(
+                    1, factory.createValue(1.0).getDate().getTimeInMillis());
+
+            try {
+                factory.createValue(1.0).getBoolean();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test date conversions.
+     */
+    public void testDateConversions() {
+        try {
+            Calendar date = Calendar.getInstance();
+            assertEquals(
+                    date.getTimeInMillis(),
+                    factory.createValue(date).getLong());
+            String re =
+                "-?\\d+-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}"
+                + "(Z|[\\+\\-]\\d{2}:\\d{2})";
+            assertTrue(factory.createValue(date).getString().matches(re));
+
+            try {
+                factory.createValue(date).getBoolean();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test long conversions.
+     */
+    public void testLongConversions() {
+        try {
+            assertEquals("1", factory.createValue(1).getString());
+            assertEquals(1.0, factory.createValue(1).getDouble(), 0.0);
+            assertEquals(1, factory.createValue(1).getDate().getTimeInMillis());
+
+            try {
+                factory.createValue(1).getBoolean();
+                fail();
+            } catch (ValueFormatException e) {
+            }
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test binary conversions.
+     */
+    public void testBinaryConversions() throws UnsupportedEncodingException {
+        try {
+            InputStream stream;
+            stream = new ByteArrayInputStream("true".getBytes("UTF-8"));
+            assertEquals(true, factory.createValue(stream).getBoolean());
+            Calendar date = Calendar.getInstance();
+            stream = new ByteArrayInputStream(
+                    factory.createValue(date).getString().getBytes("UTF-8"));
+            assertEquals(
+                    date.getTimeInMillis(),
+                    factory.createValue(stream).getDate().getTimeInMillis());
+            stream = new ByteArrayInputStream("1.0".getBytes("UTF-8"));
+            assertEquals(1.0, factory.createValue(stream).getDouble(), 0.0);
+            stream = new ByteArrayInputStream("1".getBytes("UTF-8"));
+            assertEquals(1, factory.createValue(stream).getLong());
+            stream = new ByteArrayInputStream("foo".getBytes("UTF-8"));
+            assertEquals("foo", factory.createValue(stream).getString());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test string conversions.
+     */
+    public void testStringConversions() throws UnsupportedEncodingException {
+        try {
+            assertEquals(true, factory.createValue("true").getBoolean());
+            Calendar date = Calendar.getInstance();
+            String string = factory.createValue(date).getString();
+            assertEquals(
+                    date.getTimeInMillis(),
+                    factory.createValue(string).getDate().getTimeInMillis());
+            assertEquals(1.0, factory.createValue("1.0").getDouble(), 0.0);
+            assertEquals(1, factory.createValue("1").getLong());
+            InputStream stream = factory.createValue("foo").getStream();
+            assertEquals("foo", factory.createValue(stream).getString());
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test equality comparisons.
+     */
+    public void testEquals() throws UnsupportedEncodingException {
+        try {
+            assertEquals(factory.createValue(true), factory.createValue(true));
+            assertFalse(factory.createValue(true).equals(factory.createValue(false)));
+
+            assertEquals(factory.createValue("foo"), factory.createValue("foo"));
+            assertFalse(factory.createValue("foo").equals(factory.createValue("bar")));
+
+            assertFalse(factory.createValue("true").equals(factory.createValue(true)));
+
+            assertEquals(
+                    factory.createValue(factory.createValue("foo").getStream()),
+                    factory.createValue(factory.createValue("foo").getStream()));
+        } catch (RepositoryException e) {
+            fail();
+        }
+    }
+
+    /**
+     * Test state changes.
+     */
+    public void testStateChanges() {
+        try {
+            Value value;
+
+            value = factory.createValue("foo");
+            value.getString();
+            try {
+                value.getStream();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue("foo");
+            value.getStream();
+            try {
+                value.getString();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue("foo");
+            value.equals(factory.createValue("foo"));
+            try {
+                value.getStream();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue("foo");
+            value.equals(factory.createValue(true));
+            try {
+                value.getStream();
+            } catch (IllegalStateException e) {
+                fail();
+            }
+
+            value = factory.createValue(factory.createValue("foo").getStream());
+            value.getStream();
+            try {
+                value.getString();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue(factory.createValue("foo").getStream());
+            value.getString();
+            try {
+                value.getStream();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue(factory.createValue("foo").getStream());
+            value.equals(factory.createValue(factory.createValue("foo").getStream()));
+            try {
+                value.getStream();
+                fail();
+            } catch (IllegalStateException e) {
+            }
+
+            value = factory.createValue(factory.createValue("foo").getStream());
+            value.equals(factory.createValue(true));
+            try {
+                value.getStream();
+            } catch (IllegalStateException e) {
+                fail();
+            }
+        } catch (RepositoryException e) {
+        }
+    }
+
+}

Property changes on: src/test/java/org/apache/jackrabbit/value/GenericValueFactoryTest.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/test/java/org/apache/jackrabbit/value/SimpleNamespaceResolver.java
===================================================================
--- src/test/java/org/apache/jackrabbit/value/SimpleNamespaceResolver.java	(revision 0)
+++ src/test/java/org/apache/jackrabbit/value/SimpleNamespaceResolver.java	(revision 0)
@@ -0,0 +1,88 @@
+/*
+ * 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.value;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.NamespaceException;
+
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.NameFormat;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+
+public class SimpleNamespaceResolver implements NamespaceResolver {
+
+    private final Map namespaces;
+
+    public SimpleNamespaceResolver(Map namespaces) {
+        this.namespaces = namespaces;
+    }
+
+    public SimpleNamespaceResolver() {
+        this(new HashMap());
+    }
+
+    public void setNamespace(String prefix, String uri) {
+        try {
+            String previous = getPrefix(uri);
+            if (!prefix.equals(previous)) {
+                // Different previous mapping, change
+                namespaces.remove(previous);
+                namespaces.put(prefix, uri);
+            }
+        } catch (NamespaceException e) {
+            // No previous mapping, add
+            namespaces.put(prefix, uri);
+        }
+    }
+
+    //---------------------------------------------------< NamespaceResolver >
+
+    public String getURI(String prefix) throws NamespaceException {
+        String uri = (String) namespaces.get(prefix);
+        if (uri != null) {
+            return uri;
+        }
+        throw new NamespaceException("Namespace prefix not found: " + prefix);
+    }
+
+    public String getPrefix(String uri) throws NamespaceException {
+        Iterator iterator = namespaces.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            if (uri.equals(entry.getValue())) {
+                return (String) entry.getKey();
+            }
+        }
+        throw new NamespaceException("Namespace URI not found: " + uri);
+    }
+
+    public String getJCRName(QName qName) throws NoPrefixDeclaredException {
+        return NameFormat.format(qName, this);
+    }
+
+    public QName getQName(String jcrName) throws IllegalNameException,
+            UnknownPrefixException {
+        return NameFormat.parse(jcrName, this);
+    }
+
+}

Property changes on: src/test/java/org/apache/jackrabbit/value/SimpleNamespaceResolver.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedPathValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedPathValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedPathValue.java	(revision 0)
@@ -0,0 +1,69 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed unprefixed path value. Unprefixed paths are easy to handle as
+ * no prefix remappings can affect the string representation of the value.
+ * See the {@link CommittedPrefixPathValue} for handling of prefixed paths.
+ */
+class CommittedPathValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -5120250320782666903L;
+
+    /**
+     * Unprefixed path.
+     */
+    private final String value;
+
+    /**
+     * Creates a committed unprefixed path value.
+     *
+     * @param value unprefixed path
+     */
+    CommittedPathValue(String value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#PATH}.
+     *
+     * @return {@link PropertyType#PATH}
+     */
+    public int getType() {
+        return PropertyType.PATH;
+    }
+
+    /**
+     * Returns the unprefixed path.
+     *
+     * @return unprefixed path
+     */
+    public String getString() {
+        return value;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedPathValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/GenericValueFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/GenericValueFactory.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/GenericValueFactory.java	(revision 0)
@@ -0,0 +1,184 @@
+/*
+ * 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.value;
+
+import java.io.InputStream;
+import java.util.Calendar;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.SessionNamespaceResolver;
+
+/**
+ * Generic value factory implementation. The value instances created by this
+ * factory correctly implement the full value state behaviour and
+ * {@link Object#equals(Object)} semantics specified by JSR 170.
+ * <p>
+ * The internal value representation of committed value states and
+ * value type conversion is delegated to an associated value factory instance
+ * that is expected to always return values in the committed state.
+ * <p>
+ * A {@link Session} implementation can use this class as follows:
+ * <pre>
+ * class MySession implements Session {
+ *
+ *     private final ValueFactory valueFactory;
+ *
+ *     MySession() {
+ *         valueFactory = new GenericValueFactory(this);
+ *     }
+ *
+ *     public ValueFactory getValueFactory() {
+ *         return valueFactory;
+ *     }
+ *
+ * }
+ * </pre>
+ */
+public class GenericValueFactory implements ValueFactory {
+
+    /**
+     * Committed value factory. Used for creating the committed value states.
+     */
+    private final ValueFactory factory;
+
+    /**
+     * Creates a generic value factory.
+     *
+     * @param factory committed value factory
+     */
+    public GenericValueFactory(ValueFactory factory) {
+        this.factory = factory;
+    }
+
+    /**
+     * Creates a generic value factory with a {@link CommittedValueFactory}
+     * instance based on the given namespace resolver as the committed value
+     * factory.
+     *
+     * @param resolver namespace resolver
+     */
+    public GenericValueFactory(NamespaceResolver resolver) {
+        this(new CommittedValueFactory(resolver));
+    }
+
+    /**
+     * Creates a generic value factory with a {@link CommittedValueFactory}
+     * instance based on a {@link SessionNamespaceResolver} the committed value
+     * factory.
+     *
+     * @param session session
+     */
+    public GenericValueFactory(Session session) {
+        this(new SessionNamespaceResolver(session));
+    }
+
+    //--------------------------------------------------------< ValueFactory >
+
+    /**
+     * Creates a generic string value.
+     *
+     * @param value string value
+     * @return generic string value
+     */
+    public Value createValue(String value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic long value.
+     *
+     * @param value long value
+     * @return generic long value
+     */
+    public Value createValue(long value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic double value.
+     *
+     * @param value double value
+     * @return generic double value
+     */
+    public Value createValue(double value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic boolean value.
+     *
+     * @param value boolean value
+     * @return generic boolean value
+     */
+    public Value createValue(boolean value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic date value.
+     *
+     * @param value date value
+     * @return generic date value
+     */
+    public Value createValue(Calendar value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic binary value.
+     *
+     * @param value binary value
+     * @return generic binary value
+     */
+    public Value createValue(InputStream value) {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic reference value.
+     *
+     * @param value referenced node
+     * @return generic node value
+     * @throws RepositoryException if the node is not referenceable,
+     *                             or if the reference could not be created
+     */
+    public Value createValue(Node value) throws RepositoryException {
+        return new GenericValue(factory.createValue(value), factory);
+    }
+
+    /**
+     * Creates a generic value of the given type.
+     *
+     * @param value string representation of the value
+     * @param type value type
+     * @return generic value
+     * @throws ValueFormatException if value conversion fails
+     * @see PropertyType
+     */
+    public Value createValue(String value, int type)
+            throws ValueFormatException {
+        return new GenericValue(factory.createValue(value, type), factory);
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/GenericValueFactory.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedPrefixNameValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedPrefixNameValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedPrefixNameValue.java	(revision 0)
@@ -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.value;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+
+/**
+ * Committed prefixed name value.
+ */
+class CommittedPrefixNameValue extends CommittedScalarValue
+        implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 6235905509583018237L;
+
+    /**
+     * Prefixed name.
+     */
+    private String name;
+
+    /**
+     * Namespace prefix.
+     */
+    private String prefix;
+
+    /**
+     * Namespace URI.
+     */
+    private final String namespace;
+
+    /**
+     * Namespace resolver. This field is transient to prevent serialization.
+     * During deserialization the {@link CommittedValueFactory} class is used
+     * to retrieve the current namespace resolver to be associated with the
+     * deserialized object. If a current namespace resolver is not set, then
+     * the prefix of the deserialized name will never change.
+     */
+    private transient NamespaceResolver resolver;
+
+    /**
+     * Creates a committed prefixed name value.
+     *
+     * @param name original prefixed name
+     * @param prefix original prefix
+     * @param namespace namespace URI
+     * @param resolver namespace resolver
+     */
+    CommittedPrefixNameValue(
+            String name, String prefix, String namespace,
+            NamespaceResolver resolver) {
+        this.name = name;
+        this.prefix = prefix;
+        this.namespace = namespace;
+        this.resolver = resolver;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#NAME}.
+     *
+     * @return {@link PropertyType#NAME}
+     */
+    public int getType() {
+        return PropertyType.NAME;
+    }
+
+    /**
+     * Returns the prefixed name. Rewrites the name if the previous prefix
+     * differs from the current prefix associated with the namespace URI.
+     *
+     * @return prefixed name
+     * @throws RepositoryException if namespace resolution fails
+     */
+    public String getString() throws RepositoryException {
+        if (resolver != null) {
+            String current = resolver.getPrefix(namespace);
+            if (!prefix.equals(current)) {
+                name = current + name.substring(prefix.length());
+                prefix = current;
+            }
+        }
+        return name;
+    }
+
+    //--------------------------------------------------------< Serializable >
+
+    /**
+     * Uses the default deserialization to reads the value from the object
+     * stream and then associates the value with the current namespace
+     * resolver from the {@link CommittedValueFactory} class.
+     *
+     * @param stream object stream
+     * @throws IOException if the serialized object could not be read
+     * @throws ClassNotFoundException if a serialized class is not found
+     */
+    private void readObject(ObjectInputStream stream)
+            throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        resolver = CommittedValueFactory.getCurrentResolver();
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedPrefixNameValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedReferenceValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedReferenceValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedReferenceValue.java	(revision 0)
@@ -0,0 +1,68 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed reference value.
+ */
+class CommittedReferenceValue extends CommittedScalarValue
+        implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -251816646031815951L;
+
+    /**
+     * Reference value.
+     */
+    private final String value;
+
+    /**
+     * Creates a committed reference value.
+     *
+     * @param value reference value
+     */
+    CommittedReferenceValue(String value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#REFERENCE}.
+     *
+     * @return {@link PropertyType#REFERENCE}
+     */
+    public int getType() {
+        return PropertyType.REFERENCE;
+    }
+
+    /**
+     * Returns the reference value.
+     *
+     * @return reference value
+     */
+    public String getString() {
+        return value;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedReferenceValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/SerializableInputStream.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/SerializableInputStream.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/SerializableInputStream.java	(revision 0)
@@ -0,0 +1,199 @@
+/*
+ * 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.value;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * Decorator, that makes input streams be serializable and to automatically
+ * close themselves when the last byte has been read.
+ * <p>
+ * This class contains a transient reference to a normal {@link InputStream}
+ * instance. The standard stream access methods are simply delegated to the
+ * referenced stream until it is closed either implicitly when detecting the
+ * end of the stream or explicitly when the {@link #close()} method is called.
+ * When the stream gets closed, the {@link #stream} reference is set to
+ * <code>null</code> and the stream access methods switch to an end-of-stream
+ * mode.
+ * <p>
+ * During serialization the entire remaining stream contents are read into an
+ * in-memory buffer that is then serialized as a single instance. The consumed
+ * and closed stream is replaced by a {@link ByteArrayInputStream} instance
+ * based on the content buffer to avoid modifying the external state of the
+ * stream.
+ */
+class SerializableInputStream extends InputStream implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -8914357720474921367L;
+
+    /**
+     * Underlying binary stream. Set to <code>null</code> when the stream
+     * is closed.
+     */
+    private transient InputStream stream;
+
+    /**
+     * Creates a new serializable decorator for the given binary stream.
+     *
+     * @param stream binary stream
+     */
+    public SerializableInputStream(InputStream stream) {
+        this.stream = stream;
+    }
+
+    /**
+     * Closes the underlying binary stream. Does nothing if the stream has
+     * already been closed.
+     *
+     * @throws IOException if the stream could not be closed
+     */
+    public void close() throws IOException {
+        if (stream != null) {
+            stream.close();
+            stream = null;
+        }
+    }
+
+    /**
+     * Returns the number of available bytes in the underlying stream, or
+     * <code>0</code> if the stream has closed.
+     *
+     * @return number of available bytes, or <code>0</code>
+     * @throws IOException if the number of available bytes is unavailable
+     */
+    public int available() throws IOException {
+        if (stream != null) {
+            return stream.available();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Reads and returns a single byte from the underlying input stream.
+     * Closes the stream automatically if no more bytes are available.
+     *
+     * @return read byte, or <code>-1</code> if no more bytes are available
+     * @throws IOException if the stream can not be read
+     */
+    public int read() throws IOException {
+        if (stream != null) {
+            int rv = stream.read();
+            if (rv == -1) {
+                close();
+            }
+            return rv;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Reads and returns a sequence bytes from the underlying input stream.
+     * Closes the stream automatically if no more bytes are available.
+     *
+     * @param b byte buffer
+     * @param off buffer offset
+     * @param len maximum number of bytes to read
+     * @return number of bytes read,
+     *         or <code>-1</code> if no more bytes are available
+     * @throws IOException if the stream can not be read
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (stream != null) {
+            int rv = stream.read(b, off, len);
+            if (rv == -1) {
+                close();
+            }
+            return rv;
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * Skips at most the given number of bytes and returns the number of
+     * bytes skipped. Returns <code>0</code> if the stream has closed.
+     *
+     * @param n maximum number of bytes to skip
+     * @return number of bytes skipped
+     * @throws IOException if skipping failed
+     */
+    public long skip(long n) throws IOException {
+        if (stream != null) {
+            return stream.skip(n);
+        } else {
+            return 0;
+        }
+    }
+
+    //--------------------------------------------------------< Serializable >
+
+    /**
+     * Deserializes a stream by reading it as a byte array.
+     *
+     * @param input object stream
+     * @throws IOException if deserialization fails
+     * @throws ClassNotFoundException if a deserialized class is not found
+     */
+    private void readObject(ObjectInputStream input)
+            throws IOException, ClassNotFoundException {
+        input.defaultReadObject();
+        byte[] bytes = (byte[]) input.readObject();
+        if (bytes != null) {
+            stream = new ByteArrayInputStream(bytes);
+        } else {
+            stream = null;
+        }
+    }
+
+    /**
+     * Serializes the stream by reading (the rest of) the stream contents
+     * into a byte array and serializing that.
+     *
+     * @param output object stream
+     * @throws IOException if serialization fails
+     */
+    private void writeObject(ObjectOutputStream output) throws IOException {
+        output.defaultWriteObject();
+        byte[] bytes = null;
+        if (stream != null) {
+            try {
+                bytes = new byte[4096];
+                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+                for (int n = stream.read(bytes); n != -1; n = stream.read(bytes)) {
+                    buffer.write(bytes, 0, n);
+                }
+                bytes = buffer.toByteArray();
+            } finally {
+                stream.close();
+            }
+            stream = new ByteArrayInputStream(bytes);
+        }
+        output.writeObject(bytes);
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/SerializableInputStream.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedLongValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedLongValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedLongValue.java	(revision 0)
@@ -0,0 +1,97 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed long value.
+ */
+class CommittedLongValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 3491104429903010061L;
+
+    /**
+     * Long value.
+     */
+    private final long value;
+
+    /**
+     * Creates a committed long value.
+     *
+     * @param value long value
+     */
+    CommittedLongValue(long value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#LONG}.
+     *
+     * @return {@link PropertyType#LONG}
+     */
+    public int getType() {
+        return PropertyType.LONG;
+    }
+
+    /**
+     * Returns the long value as a date.
+     *
+     * @return date value
+     */
+    public Calendar getDate() {
+        Calendar date = Calendar.getInstance();
+        date.setTimeInMillis(value);
+        return date;
+    }
+
+    /**
+     * Returns the long value as a double.
+     *
+     * @return double value
+     */
+    public double getDouble() {
+        return (double) value;
+    }
+
+    /**
+     * Returns the long value.
+     *
+     * @return long value
+     */
+    public long getLong() {
+        return value;
+    }
+
+    /**
+     * Returns the long value as a string.
+     *
+     * @return string value
+     */
+    public String getString() {
+        return Long.toString(value);
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedLongValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedScalarValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedScalarValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedScalarValue.java	(revision 0)
@@ -0,0 +1,104 @@
+/*
+ * 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.value;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Calendar;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+/**
+ * Abstract base class for committed non-binary values. This class throws
+ * an {@link IllegalStateException} from {@link #getStream()} and defaults
+ * to throwing a {@link ValueFormatException} from the other value getters.
+ * Subclasses should implement {@link Value#getType()} and the non-binary
+ * value getters for which a value conversion is defined.
+ */
+abstract class CommittedScalarValue implements Value, Serializable {
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Throws a {@link ValueFormatException}, unless overridden by a subclass.
+     *
+     * @return nothing
+     * @throws ValueFormatException always thrown, unless overridden
+     */
+    public boolean getBoolean() throws ValueFormatException {
+        throw new ValueFormatException("Invalid boolean format: " + this);
+    }
+
+    /**
+     * Throws a {@link ValueFormatException}, unless overridden by a subclass.
+     *
+     * @return nothing
+     * @throws ValueFormatException always thrown, unless overridden
+     */
+    public Calendar getDate() throws ValueFormatException {
+        throw new ValueFormatException("Invalid date format: " + this);
+    }
+
+    /**
+     * Throws a {@link ValueFormatException}, unless overridden by a subclass.
+     *
+     * @return nothing
+     * @throws ValueFormatException always thrown, unless overridden
+     */
+    public double getDouble() throws ValueFormatException {
+        throw new ValueFormatException("Invalid double format: " + this);
+    }
+
+    /**
+     * Throws a {@link ValueFormatException}, unless overridden by a subclass.
+     *
+     * @return nothing
+     * @throws ValueFormatException always thrown, unless overridden
+     */
+    public long getLong() throws ValueFormatException {
+        throw new ValueFormatException("Invalid long format: " + this);
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public InputStream getStream() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    //--------------------------------------------------------------< Object >
+
+    /**
+     * Returns the string representation of this value.
+     *
+     * @return return value of {@link Value#getString()},
+     *         or <code>&lt;value&gt;</code> if the method fails
+     */
+    public String toString() {
+        try {
+            return getString();
+        } catch (RepositoryException e) {
+            return "<value>";
+        }
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedScalarValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/GenericValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/GenericValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/GenericValue.java	(revision 0)
@@ -0,0 +1,596 @@
+/*
+ * 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.value;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+/**
+ * Generic value implementation. This class uses the State design pattern
+ * to implement the full value state mechanism specified by JSR 170. This
+ * class also implements the correct {@link Object#equals(Object)} semantics.
+ * <p>
+ * The internal value representation and the value conversion rules are
+ * delegated to the associated committed value states and a value factory
+ * for creating those states.
+ */
+class GenericValue implements Value, Serializable {
+
+    /**
+     * Serial value UID.
+     */
+    private static final long serialVersionUID = -1087747028146744179L;
+
+    /**
+     * UTF-8 character encoding.
+     */
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+
+    /**
+     * Value type. The type of the generic value does not change even if
+     * the value state changes from binary to non-binary or vice versa.
+     */
+    private final int type;
+
+    /**
+     * Value state. The value state is initially set to an instance of
+     * the {@link InitialBinaryValue} or {@link InitialScalarValue} class,
+     * and changed to a direct reference to an underlying committed value
+     * state when the first value getter is called. The value state is not
+     * changed after the first getter invocation.
+     */
+    private Value value;
+
+    /**
+     * Creates a generic value. The given value factory is used if value
+     * state conversions are needed. The value factory must create value
+     * instances that are already in the committed state.
+     *
+     * @param committed committed value state
+     * @param factory value factory for potential state conversions
+     */
+    GenericValue(Value committed, ValueFactory factory) {
+        this.type = committed.getType();
+        if (type == PropertyType.BINARY) {
+            this.value = new InitialBinaryValue(committed, factory);
+        } else {
+            this.value = new InitialScalarValue(committed, factory);
+        }
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns the value type. The type never changes even if the underlying
+     * value state may change.
+     *
+     * @return value type
+     * @see PropertyType
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Returns the boolean value. Commits the value state to non-binary if this
+     * is the first value getter call.
+     *
+     * @return boolean value
+     * @throws IllegalStateException if the value is in committed binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public boolean getBoolean()
+            throws IllegalStateException, RepositoryException {
+        return value.getBoolean();
+    }
+
+    /**
+     * Returns the date value. Commits the value state to non-binary if this
+     * is the first value getter call.
+     *
+     * @return date value
+     * @throws IllegalStateException if the value is in committed binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public Calendar getDate()
+            throws IllegalStateException, RepositoryException {
+        return value.getDate();
+    }
+
+    /**
+     * Returns the double value. Commits the value state to non-binary if this
+     * is the first value getter call.
+     *
+     * @return double value
+     * @throws IllegalStateException if the value is in committed binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public double getDouble()
+            throws IllegalStateException, RepositoryException {
+        return value.getDouble();
+    }
+
+    /**
+     * Returns the long value. Commits the value state to non-binary if this
+     * is the first value getter call.
+     *
+     * @return long value
+     * @throws IllegalStateException if the value is in committed binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public long getLong()
+            throws IllegalStateException, RepositoryException {
+        return value.getLong();
+    }
+
+    /**
+     * Returns the binary value. Commits the value state to binary if this
+     * is the first value getter call.
+     *
+     * @return long value
+     * @throws IllegalStateException
+     *             if the value is in committed non-binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public InputStream getStream()
+            throws IllegalStateException, RepositoryException {
+        return value.getStream();
+    }
+
+    /**
+     * Returns the string value. Commits the value state to non-binary if this
+     * is the first value getter call.
+     *
+     * @return string value
+     * @throws IllegalStateException if the value is in committed binary state
+     * @throws RepositoryException if value access or conversion fails
+     */
+    public String getString()
+            throws IllegalStateException, RepositoryException {
+        return value.getString();
+    }
+
+    //--------------------------------------------------------------< Object >
+
+    /**
+     * Compares two values for equality. Implements the exact equality
+     * semantics specified by JSR 170, i.e. values are equal if and only if
+     * their types and string representations are equal.
+     * <p>
+     * Returns <code>false</code> if either this or the other value is in
+     * committed binary state (in which case the string representation is
+     * not available) or if one of the string value getters fails.
+     * <p>
+     * Note that this method may change the underlying value state!
+     *
+     * @param other other object
+     * @return <code>true</code> if the values are equal,
+     *         <code>false</code> otherwise
+     */
+    public boolean equals(Object other) {
+        try {
+            return (other instanceof Value)
+                && getType() == ((Value) other).getType()
+                && getString().equals(((Value) other).getString());
+        } catch (IllegalStateException e) {
+            return false;
+        } catch (RepositoryException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the hash code of this value. The hash code is computed based
+     * on the value type and string representation to be consistent with the
+     * {@link #equals(Object)} implementation.
+     * <p>
+     * Returns <code>0</code> if this value is in commmitted binary state
+     * (in which case the string representation is not available) or if the
+     * string value getter fails.
+     * <p>
+     * Note that this method may change the underlying value state!
+     *
+     * @return hash code
+     */
+    public int hashCode() {
+        try {
+            int code = 17;
+            code = 37 * code + getType();
+            code = 37 * code + getString().hashCode();
+            return code;
+        } catch (IllegalStateException e) {
+            return 0;
+        } catch (RepositoryException e) {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns a string representation (not necessarily {@link #getString()})
+     * of the underlying value. Does <em>not</em> change the value state.
+     *
+     * @return string representation of the value
+     */
+    public String toString() {
+        return value.toString();
+    }
+
+    //--------------------------------------------------< InitialBinaryValue >
+
+    /**
+     * Initial state of a binary value. The task of this class is to intercept
+     * the first binary or non-binary value getter method and to change the
+     * state of the containing generic value accordingly.
+     */
+    private class InitialBinaryValue implements Value, Serializable {
+
+        /**
+         * Serial version UID.
+         */
+        private static final long serialVersionUID = 2525692776896914509L;
+
+        /**
+         * Underlying committed value state.
+         */
+        private final Value committed;
+
+        /**
+         * Committed value factory for the potential scalar value conversion.
+         */
+        private transient ValueFactory factory;
+
+        /**
+         * Creates an initial binary value state. The given value factory instance
+         * must create value instances that are already in the committed state.
+         *
+         * @param committed underlying committed binary value state
+         * @param factory value factory for the potential scalar value conversion
+         */
+        public InitialBinaryValue(Value committed, ValueFactory factory) {
+            this.committed = committed;
+            this.factory = factory;
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to a string
+         * by reading it as a {@link #UTF8 UTF-8} stream.
+         *
+         * @return string contents of the stream
+         * @throws RepositoryException if the stream could not be read as UTF-8
+         */
+        private Value toStringValue() throws RepositoryException {
+            try {
+                Reader reader =
+                    new InputStreamReader(committed.getStream(), UTF8);
+                try {
+                    StringWriter writer = new StringWriter();
+                    char[] buffer = new char[1000];
+                    int n = reader.read(buffer);
+                    while (n != -1) {
+                        writer.write(buffer, 0, n);
+                        n = reader.read(buffer);
+                    }
+                    return factory.createValue(writer.toString());
+                } finally {
+                    reader.close();
+                }
+            } catch (IOException e) {
+                throw new RepositoryException("Error reading binary value", e);
+            }
+        }
+
+        //-----------------------------------------------------------< Value >
+
+        /**
+         * Returns {@link PropertyType#BINARY}.
+         *
+         * @return {@link PropertyType#BINARY}
+         */
+        public int getType() {
+            return PropertyType.BINARY;
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to boolean by
+         * reading the stream as UTF-8 and using the
+         * {@link #factory associated value factory}. Sets the boolean value as
+         * the committed non-binary state of the containing generic value and
+         * restarts the method call.
+         *
+         * @return boolean value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public boolean getBoolean() throws RepositoryException {
+            value = toStringValue();
+            return value.getBoolean();
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to date by
+         * reading the stream as UTF-8 and using the
+         * {@link #factory associated value factory}. Sets the date value as
+         * the committed non-binary state of the containing generic value and
+         * restarts the method call.
+         *
+         * @return date value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public Calendar getDate() throws RepositoryException {
+            value = toStringValue();
+            return value.getDate();
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to double by
+         * reading the stream as UTF-8 and using the
+         * {@link #factory associated value factory}. Sets the double value as
+         * the committed non-binary state of the containing generic value and
+         * restarts the method call.
+         *
+         * @return double value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public double getDouble() throws RepositoryException {
+            value = toStringValue();
+            return value.getDouble();
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to long by
+         * reading the stream as UTF-8 and using the
+         * {@link #factory associated value factory}. Sets the long value as
+         * the committed non-binary state of the containing generic value and
+         * restarts the method call.
+         *
+         * @return long value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public long getLong() throws RepositoryException {
+            value = toStringValue();
+            return value.getLong();
+        }
+
+        /**
+         * Sets the {@link #committed underlying binary value} as the committed
+         * state of the containing generic value and restarts the method call.
+         *
+         * @return binary value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public InputStream getStream() throws RepositoryException {
+            value = committed;
+            return value.getStream();
+        }
+
+        /**
+         * Converts the {@link #committed underlying binary value} to string by
+         * reading the stream as UTF-8 and using the
+         * {@link #factory associated value factory}. Sets the string value as
+         * the committed non-binary state of the containing generic value and
+         * restarts the method call.
+         *
+         * @return string value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public String getString() throws RepositoryException {
+            value = toStringValue();
+            return value.getString();
+        }
+
+        //----------------------------------------------------< Serializable >
+
+        /**
+         * Uses the default deserialization to reads the values from the object
+         * stream and then associates the instance with the current
+         * {@link CommittedValueFactory} instance.
+         *
+         * @param stream object stream
+         * @throws IOException if the serialized object could not be read
+         * @throws ClassNotFoundException if a serialized class is not found
+         */
+        private void readObject(ObjectInputStream stream)
+                throws IOException, ClassNotFoundException {
+            stream.defaultReadObject();
+            factory = CommittedValueFactory.getCurrentFactory();
+        }
+
+        //----------------------------------------------------------< Object >
+
+        /**
+         * Returns "<code>&lt;binary&gt;</code>".
+         *
+         * @return "<code>&lt;binary&gt;</code>"
+         */
+        public String toString() {
+            return "<binary>";
+        }
+
+    }
+
+    //--------------------------------------------------< InitialScalarValue >
+
+    /**
+     * Initial state of a non-binary value. The task of this class is to
+     * intercept the first binary or non-binary value getter method and to
+     * change the state of the containing generic value accordingly.
+     */
+    private class InitialScalarValue implements Value, Serializable {
+
+        /**
+         * Serial version UID.
+         */
+        private static final long serialVersionUID = 7021172028939318405L;
+
+        /**
+         * Underlying committed value state.
+         */
+        private final Value committed;
+
+        /**
+         * Committed value factory for the potential binary value conversion.
+         */
+        private transient ValueFactory factory;
+
+        /**
+         * Creates an initial scalar value state. The given value factory
+         * instance must create value instances that are already in the
+         * committed state.
+         *
+         * @param value underlying committed scalar value state
+         * @param factory value factory for potential value conversion
+         */
+        public InitialScalarValue(Value value, ValueFactory factory) {
+            this.committed = value;
+            this.factory = factory;
+        }
+
+        //---------------------------------------------------------------< Value >
+
+        /**
+         * Returns the type of the {@link #committed underlying value}.
+         *
+         * @return value type
+         */
+        public int getType() {
+            return committed.getType();
+        }
+
+        /**
+         * Sets the {@link #committed underlying scalar value} as the committed
+         * non-binary state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return boolean value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public boolean getBoolean() throws RepositoryException {
+            value = committed;
+            return value.getBoolean();
+        }
+
+        /**
+         * Sets the {@link #committed underlying scalar value} as the committed
+         * non-binary state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return date value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public Calendar getDate() throws RepositoryException {
+            value = committed;
+            return value.getDate();
+        }
+
+        /**
+         * Sets the {@link #committed underlying scalar value} as the committed
+         * non-binary state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return double value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public double getDouble() throws RepositoryException {
+            value = committed;
+            return value.getDouble();
+        }
+
+        /**
+         * Sets the {@link #committed underlying scalar value} as the committed
+         * non-binary state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return long value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public long getLong() throws RepositoryException {
+            value = committed;
+            return value.getLong();
+        }
+
+        /**
+         * Converts the {@link #committed underlying scalar value} to binary
+         * using the string representation of the value and the
+         * {@link #factory associated value factory}. Sets the binary value as
+         * the committed state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return binary value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public InputStream getStream() throws RepositoryException {
+            value = factory.createValue(
+                    committed.getString(), PropertyType.BINARY);
+            return value.getStream();
+        }
+
+        /**
+         * Sets the {@link #committed underlying scalar value} as the committed
+         * non-binary state of the containing generic value and restarts the
+         * method call.
+         *
+         * @return string value
+         * @throws RepositoryException if the value retrieval fails
+         */
+        public String getString() throws RepositoryException {
+            value = committed;
+            return value.getString();
+        }
+
+        //----------------------------------------------------< Serializable >
+
+        /**
+         * Uses the default deserialization to read the values from the object
+         * stream and then associates the instance with the current
+         * {@link CommittedValueFactory} instance.
+         *
+         * @param stream object stream
+         * @throws IOException if the serialized object could not be read
+         * @throws ClassNotFoundException if a serialized class is not found
+         */
+        private void readObject(ObjectInputStream stream)
+                throws IOException, ClassNotFoundException {
+            stream.defaultReadObject();
+            factory = CommittedValueFactory.getCurrentFactory();
+        }
+
+        //----------------------------------------------------------< Object >
+
+        /**
+         * Returns the string representation of the
+         * {@link #committed underlying value}.
+         *
+         * @return string representation of the value
+         */
+        public String toString() {
+            return committed.toString();
+        }
+
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/GenericValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedStringValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedStringValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedStringValue.java	(revision 0)
@@ -0,0 +1,110 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+import javax.jcr.ValueFormatException;
+
+/**
+ * Committed string value.
+ */
+class CommittedStringValue extends CommittedScalarValue
+        implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 8917367030023376511L;
+
+    /**
+     * String value.
+     */
+    private final String value;
+
+    /**
+     * Creates a committed string value.
+     *
+     * @param value string value
+     */
+    CommittedStringValue(String value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#STRING}.
+     *
+     * @return {@link PropertyType#STRING}
+     */
+    public int getType() {
+        return PropertyType.STRING;
+    }
+
+    /**
+     * Returns the string value as a boolean.
+     *
+     * @return boolean value
+     * @throws ValueFormatException if value conversion fails
+     */
+    public boolean getBoolean() throws ValueFormatException {
+        return new ValueParser(value).getBoolean();
+    }
+
+    /**
+     * Returns the string value as a date.
+     *
+     * @return date value
+     * @throws ValueFormatException if value conversion fails
+     */
+    public Calendar getDate() throws ValueFormatException {
+        return new ValueParser(value).getDate();
+    }
+
+    /**
+     * Returns the string value as a double.
+     *
+     * @return double value
+     * @throws ValueFormatException if value conversion fails
+     */
+    public double getDouble() throws ValueFormatException {
+        return new ValueParser(value).getDouble();
+    }
+
+    /**
+     * Returns the string value as long.
+     *
+     * @return long value
+     * @throws ValueFormatException if value conversion fails
+     */
+    public long getLong() throws ValueFormatException {
+        return new ValueParser(value).getLong();
+    }
+
+    /**
+     * Returns the string value.
+     *
+     * @return string value
+     */
+    public String getString() {
+        return value;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedStringValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedValueFactory.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedValueFactory.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedValueFactory.java	(revision 0)
@@ -0,0 +1,198 @@
+/*
+ * 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.value;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+
+/**
+ * Factory for creating {@link Value} instances that are already in the
+ * committed binary or non-binary state.
+ */
+public class CommittedValueFactory implements ValueFactory {
+
+    /**
+     * Static map of factory instances in use per thread during
+     * deserialization of serialized value instances.
+     */
+    private static final Map factories =
+        Collections.synchronizedMap(new HashMap());
+
+    public static CommittedValueFactory getCurrentFactory() {
+        return (CommittedValueFactory) factories.get(Thread.currentThread());
+    }
+
+    public static NamespaceResolver getCurrentResolver() {
+        return getCurrentFactory().resolver;
+    }
+
+    public void beginDeserialization() {
+        factories.put(Thread.currentThread(), this);
+    }
+
+    public void endDeserialization() {
+        factories.remove(Thread.currentThread());
+    }
+
+    /**
+     * Namespace resolver for handling name and path values.
+     */
+    private final NamespaceResolver resolver;
+
+    /**
+     * Creates a new committed value factory.
+     *
+     * @param resolver namespace resolver for handling name and path values
+     */
+    public CommittedValueFactory(NamespaceResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    //--------------------------------------------------------< ValueFactory >
+
+    /**
+     * Creates a committed string value.
+     *
+     * @param value string value
+     * @return {@link CommittedStringValue} instance
+     */
+    public Value createValue(String value) {
+        return new CommittedStringValue(value);
+    }
+
+    /**
+     * Creates a committed long value.
+     *
+     * @param value long value
+     * @return {@link CommittedLongValue} instance
+     */
+    public Value createValue(long value) {
+        return new CommittedLongValue(value);
+    }
+
+    /**
+     * Creates a committed double value.
+     *
+     * @param value double value
+     * @return {@link CommittedDoubleValue} instance
+     */
+    public Value createValue(double value) {
+        return new CommittedDoubleValue(value);
+    }
+
+    /**
+     * Returns one of the two static committed boolean value instances.
+     *
+     * @param value boolean value
+     * @return {@link CommittedTrueValue#INSTANCE} or
+     *         {@link CommittedFalseValue#INSTANCE}
+     */
+    public Value createValue(boolean value) {
+        if (value) {
+            return CommittedTrueValue.INSTANCE;
+        } else {
+            return CommittedFalseValue.INSTANCE;
+        }
+    }
+
+    /**
+     * Creates a committed date value.
+     *
+     * @param value date value
+     * @return {@link CommittedDateValue} instance
+     */
+    public Value createValue(Calendar value) {
+        return new CommittedDateValue(value);
+    }
+
+    /**
+     * Creates a committed binary value. The given binary stream will the
+     * returned as-is by the {@link Value#getStream()} method of the returned
+     * binary value.
+     *
+     * @param value binary value
+     * @return {@link CommittedBinaryValue} instance
+     */
+    public Value createValue(InputStream value) {
+        return new CommittedBinaryValue(value);
+    }
+
+    /**
+     * Creates a committed reference value.
+     *
+     * @param value node whose {@link Node#getUUID() UUID} is referenced
+     * @return {@link CommittedReferenceValue} instance
+     * @throws RepositoryException if the given node is not referenceable,
+     *                             or if node identifier access fails
+     */
+    public Value createValue(Node value) throws RepositoryException {
+        return new CommittedReferenceValue(value.getUUID());
+    }
+
+    /**
+     * Creates a committed value of the given type. The {@link ValueParser}
+     * class is used for all the value conversions. The associated
+     * {@link #resolver namespace resolver} is used for name and path
+     * values.
+     *
+     * @param value string representation of the value
+     * @param type value type, see {@link PropertyType}
+     * @return committed value
+     * @throws ValueFormatException if the value conversion fails,
+     *                              or if the given value type is unknown
+     */
+    public Value createValue(String value, int type)
+            throws ValueFormatException {
+        switch (type) {
+        case PropertyType.BINARY:
+            return createValue(new ValueParser(value).getStream());
+        case PropertyType.BOOLEAN:
+            return createValue(new ValueParser(value).getBoolean());
+        case PropertyType.DATE:
+            return createValue(new ValueParser(value).getDate());
+        case PropertyType.DOUBLE:
+            return createValue(new ValueParser(value).getDouble());
+        case PropertyType.LONG:
+            return createValue(new ValueParser(value).getLong());
+        case PropertyType.NAME:
+            return new ValueParser(value).createName(resolver);
+        case PropertyType.PATH:
+            return new ValueParser(value).createPath(resolver);
+        case PropertyType.REFERENCE:
+            // TODO: Check reference string format?
+            return new CommittedReferenceValue(value);
+        case PropertyType.STRING:
+            return createValue(value);
+        default:
+            throw new ValueFormatException("Unknown value type: " + type);
+        }
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedValueFactory.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedPrefixPathValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedPrefixPathValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedPrefixPathValue.java	(revision 0)
@@ -0,0 +1,176 @@
+/*
+ * 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.value;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+
+/**
+ * Committed prefixed path value.
+ */
+class CommittedPrefixPathValue extends CommittedScalarValue {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 2959434044955375643L;
+
+    /**
+     * Prefixed path.
+     */
+    private String path;
+
+    /**
+     * Prefix to namespace URI mappings used in the path.
+     */
+    private final Map namespaces;
+
+    /**
+     * Namespace resolver. This field is transient to prevent serialization.
+     * During deserialization the {@link CommittedValueFactory} class is used
+     * to retrieve the current namespace resolver to be associated with the
+     * deserialized object. If a current namespace resolver is not set, then
+     * the prefix mappings of the deserialized path will never change.
+     */
+    private transient NamespaceResolver resolver;
+
+    /**
+     * Creates a committed prefixed path value. The prefix map should
+     * not be used externally after it has been passed to this constructor.
+     *
+     * @param path prefixed path
+     * @param namespaces prefix to namespace URI mappings used in the path
+     * @param resolver namespace resolver
+     */
+    CommittedPrefixPathValue(
+            String path, Map namespaces, NamespaceResolver resolver) {
+        this.path = path;
+        this.namespaces = namespaces;
+        this.resolver = resolver;
+    }
+
+    /**
+     * Checks all the prefix mappings used in the path for potential changes.
+     * Returns a map containing all the changed prefixes as keys and the new
+     * prefixes as values. Returns an empty map if no prefix changes have
+     * occurred.
+     *
+     * @return map of prefix changes
+     * @throws NamespaceException if a namespace resolution fails
+     */
+    private Map getPrefixChanges() throws NamespaceException {
+        Map changes = new HashMap();
+        if (resolver != null) {
+            Iterator iterator = namespaces.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                String prefix = (String) entry.getKey();
+                String namespace = (String) entry.getValue();
+                String current = resolver.getPrefix(namespace);
+                if (!prefix.equals(current)) {
+                    changes.put(prefix, current);
+                }
+            }
+        }
+        return changes;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#PATH}.
+     *
+     * @return {@link PropertyType#PATH}
+     */
+    public int getType() {
+        return PropertyType.PATH;
+    }
+
+    /**
+     * Returns the prefixed path. Rewrites the path if the previous prefixes
+     * differ from the current prefixes associated with the namespace URIs
+     * referenced in the path.
+     *
+     * @return prefixed path
+     * @throws RepositoryException if namespace resolution fails
+     */
+    public String getString() throws RepositoryException {
+        Map changes = getPrefixChanges();
+
+        // If at least one prefix has changed, need to rewrite the path string
+        if (!changes.isEmpty()) {
+            Iterator iterator = changes.entrySet().iterator();
+            while (iterator.hasNext()) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                namespaces.put(
+                        entry.getValue(), namespaces.remove(entry.getKey()));
+            }
+
+            StringBuffer buffer = new StringBuffer();
+            int position = 0;
+
+            int colon = path.indexOf(':', position);
+            while (colon != -1) {
+                // Prefix found, check if it's changed
+                int slash = path.lastIndexOf('/', colon);
+                String prefix = path.substring(slash + 1, colon);
+                String current = (String) changes.get(prefix);
+                if (current != null) {
+                    buffer.append(path.substring(position, slash + 1));
+                    buffer.append(current);
+                    buffer.append(':');
+                } else {
+                    buffer.append(path.substring(0, colon + 1));
+                }
+                position = colon + 1;
+                colon = path.indexOf(':', position);
+            }
+
+            // No more prefixes, append the rest of the path
+            path = buffer.toString() + path.substring(position);
+        }
+
+        return path;
+    }
+
+    //--------------------------------------------------------< Serializable >
+
+    /**
+     * Uses the default deserialization to reads the value from the object
+     * stream and then associates the value with the current namespace
+     * resolver from the {@link CommittedValueFactory} class.
+     *
+     * @param stream object stream
+     * @throws IOException if the serialized object could not be read
+     * @throws ClassNotFoundException if a serialized class is not found
+     */
+    private void readObject(ObjectInputStream stream)
+            throws IOException, ClassNotFoundException {
+        stream.defaultReadObject();
+        resolver = CommittedValueFactory.getCurrentResolver();
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedPrefixPathValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedDoubleValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedDoubleValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedDoubleValue.java	(revision 0)
@@ -0,0 +1,98 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed double value.
+ */
+class CommittedDoubleValue extends CommittedScalarValue
+        implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -8178359961277577721L;
+
+    /**
+     * Double value.
+     */
+    private final double value;
+
+    /**
+     * Creates a committed double value.
+     *
+     * @param value double value
+     */
+    CommittedDoubleValue(double value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#DOUBLE}.
+     *
+     * @return {@link PropertyType#DOUBLE}
+     */
+    public int getType() {
+        return PropertyType.DOUBLE;
+    }
+
+    /**
+     * Returns the double value as a date.
+     *
+     * @return date value
+     */
+    public Calendar getDate() {
+        Calendar date = Calendar.getInstance();
+        date.setTimeInMillis((long) value);
+        return date;
+    }
+
+    /**
+     * Returns the double value.
+     *
+     * @return double value
+     */
+    public double getDouble() {
+        return value;
+    }
+
+    /**
+     * Returns the double value as a long.
+     *
+     * @return long value
+     */
+    public long getLong() {
+        return (long) value;
+    }
+
+    /**
+     * Returns the double value as a string.
+     *
+     * @return string value
+     */
+    public String getString() {
+        return Double.toString(value);
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedDoubleValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedDateValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedDateValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedDateValue.java	(revision 0)
@@ -0,0 +1,151 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed date value.
+ */
+class CommittedDateValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 5834756851726381466L;
+
+    /**
+     * Decimal format for two-digit numbers.
+     */
+    private static final DecimalFormat XX_FORMAT = new DecimalFormat("00");
+
+    /**
+     * Decimal format for three-digit numbers.
+     */
+    private static final DecimalFormat XXX_FORMAT = new DecimalFormat("000");
+
+    /**
+     * Decimal format for four-digit numbers.
+     */
+    private static final DecimalFormat XXXX_FORMAT = new DecimalFormat("0000");
+
+    /**
+     * Date value as milliseconds since the epoch.
+     */
+    private final long value;
+
+    /**
+     * Creates a committed date value.
+     *
+     * @param value date value
+     */
+    CommittedDateValue(Calendar value) {
+        this.value = value.getTimeInMillis();
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#DATE}.
+     *
+     * @return {@link PropertyType#DATE}
+     */
+    public int getType() {
+        return PropertyType.DATE;
+    }
+
+    /**
+     * Returns the date value. The returned value is newly instantiated using
+     * {@link Calendar#getInstance()} and can be freely modified.
+     *
+     * @return date value
+     */
+    public Calendar getDate() {
+        Calendar date = Calendar.getInstance();
+        date.setTimeInMillis(value);
+        return date;
+    }
+
+    /**
+     * Returns the date value as milliseconds since the epoch.
+     *
+     * @return milliseconds since the epoch
+     */
+    public double getDouble() {
+        return (double) value;
+    }
+
+    /**
+     * Returns the date value as milliseconds since the epoch.
+     *
+     * @return milliseconds since the epoch
+     */
+    public long getLong() {
+        return value;
+    }
+
+    /**
+     * Returns the value as a ISO8601 date/time string.
+     *
+     * @return string value
+     */
+    public String getString() {
+        StringWriter writer = new StringWriter();
+
+        Calendar date = GregorianCalendar.getInstance();
+        date.setTimeInMillis(value);
+        if (date.get(Calendar.ERA) == GregorianCalendar.BC) {
+            writer.write('-');
+        }
+        writer.write(XXXX_FORMAT.format(date.get(Calendar.YEAR)));
+        writer.write('-');
+        writer.write(XX_FORMAT.format(date.get(Calendar.MONTH) + 1));
+        writer.write('-');
+        writer.write(XX_FORMAT.format(date.get(Calendar.DAY_OF_MONTH)));
+        writer.write('T');
+        writer.write(XX_FORMAT.format(date.get(Calendar.HOUR_OF_DAY)));
+        writer.write(':');
+        writer.write(XX_FORMAT.format(date.get(Calendar.MINUTE)));
+        writer.write(':');
+        writer.write(XX_FORMAT.format(date.get(Calendar.SECOND)));
+        writer.write('.');
+        writer.write(XXX_FORMAT.format(date.get(Calendar.MILLISECOND)));
+        int offset = date.getTimeZone().getOffset(value) / (60 * 1000);
+        if (offset != 0) {
+            if (offset < 0) {
+                writer.write('-');
+                offset = -offset;
+            } else {
+                writer.write('+');
+            }
+            writer.write(XX_FORMAT.format(offset / 60));
+            writer.write(':');
+            writer.write(XX_FORMAT.format(offset % 60));
+        } else {
+            writer.write('Z');
+        }
+
+        return writer.toString();
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedDateValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedTrueValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedTrueValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedTrueValue.java	(revision 0)
@@ -0,0 +1,86 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+
+/**
+ * Committed boolean <code>true</code> value.
+ */
+class CommittedTrueValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 7566891337364047387L;
+
+    /**
+     * The singleton instance of this class.
+     */
+    public static final Value INSTANCE = new CommittedTrueValue();
+
+    /**
+     * Private constructor to enforce the singleton instance.
+     */
+    private CommittedTrueValue() {
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#BOOLEAN}.
+     *
+     * @return {@link PropertyType#BOOLEAN}
+     */
+    public int getType() {
+        return PropertyType.BOOLEAN;
+    }
+
+    /**
+     * Returns <code>true</code>.
+     *
+     * @return <code>true</code>
+     */
+    public boolean getBoolean() {
+        return true;
+    }
+
+    /**
+     * Returns "<code>true</code>".
+     *
+     * @return "<code>true</code>"
+     */
+    public String getString() {
+        return Boolean.TRUE.toString();
+    }
+
+    //--------------------------------------------------------< Serializable >
+
+    /**
+     * Enforces the singleton instance of this class by returning it as the
+     * result of deserialization.
+     *
+     * @return {@link #INSTANCE}
+     */
+    private Object readResolve() {
+        return INSTANCE;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedTrueValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedNameValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedNameValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedNameValue.java	(revision 0)
@@ -0,0 +1,69 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+
+/**
+ * Committed unprefixed name value. Unprefixed names are easy to handle as
+ * no prefix remappings can affect the string representation of the value.
+ * See the {@link CommittedPrefixNameValue} for handling of prefixed names.
+ */
+class CommittedNameValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -4823273796633809803L;
+
+    /**
+     * Unprefixed name value.
+     */
+    private final String value;
+
+    /**
+     * Creates a committed unprefixed name value.
+     *
+     * @param value unprefixed name
+     */
+    CommittedNameValue(String value) {
+        this.value = value;
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#NAME}.
+     *
+     * @return {@link PropertyType#NAME}
+     */
+    public int getType() {
+        return PropertyType.NAME;
+    }
+
+    /**
+     * Returns the unprefixed name.
+     *
+     * @return unprefixed name
+     */
+    public String getString() {
+        return value;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedNameValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedBinaryValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedBinaryValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedBinaryValue.java	(revision 0)
@@ -0,0 +1,132 @@
+/*
+ * 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.value;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Calendar;
+
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+
+/**
+ * Committed binary value. This class throws an {@link IllegalStateException}
+ * from all the value getters other than {@link #getStream()}.
+ */
+class CommittedBinaryValue implements Value, Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 6752015631871189215L;
+
+    /**
+     * The binary stream.
+     */
+    private final InputStream stream;
+
+    /**
+     * Creates a new committed binary value.
+     *
+     * @param value binary stream
+     */
+    CommittedBinaryValue(InputStream value) {
+        this.stream = new SerializableInputStream(value);
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#BINARY}.
+     *
+     * @return {@link PropertyType#BINARY}
+     */
+    public int getType() {
+        return PropertyType.BINARY;
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public boolean getBoolean() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public Calendar getDate() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public double getDouble() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public long getLong() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    /**
+     * Returns the binary stream.
+     *
+     * @return binary stream
+     */
+    public InputStream getStream() {
+        return stream;
+    }
+
+    /**
+     * Throws an {@link IllegalStateException}.
+     *
+     * @return nothing
+     * @throws IllegalStateException always thrown
+     */
+    public String getString() throws IllegalStateException {
+        throw new IllegalStateException("Illegal value state");
+    }
+
+    //--------------------------------------------------------------< Object >
+
+    /**
+     * Returns "<code>&lt;binary&gt;</code>".
+     *
+     * @return "<code>&lt;binary&gt;</code>"
+     */
+    public String toString() {
+        return "<binary>";
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedBinaryValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/ValueParser.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/ValueParser.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/ValueParser.java	(revision 0)
@@ -0,0 +1,381 @@
+/*
+ * 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.value;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+
+/**
+ * String parser for value format conversions.
+ */
+class ValueParser {
+
+    /**
+     * The UTC time zone.
+     */
+    private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
+    /**
+     * The string value being parsed.
+     */
+    private final String value;
+
+    /**
+     * Creates a string parser for value format conversion.
+     *
+     * @param value string value to be parsed
+     */
+    ValueParser(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Checks if the given character matches the non-space rule in JSR 170.
+     *
+     * @param ch character
+     * @return <code>true</code> if the character is non-space,
+     *         <code>false</code> otherwise
+     */
+    private static boolean isNonSpace(char ch) {
+        return ch != '/' && ch != ':' && ch != '[' && ch != ']'
+            && ch != '*' && ch != '\'' && ch != '"' && ch != '|'
+            && !Character.isSpaceChar(ch);
+    }
+
+    /**
+     * Checks the value of a character within a string.
+     *
+     * @param i index of the character within the string
+     * @param ch expected value of the indexed character
+     * @throws ValueFormatException if the indexed character is not as expected
+     * @throws IndexOutOfBoundsException if the index is outside the string
+     */
+    private void expect(int i, char ch)
+            throws ValueFormatException, IndexOutOfBoundsException {
+        if (value.charAt(i) != ch) {
+            throw new ValueFormatException("Invalid value format: " + value);
+        }
+    }
+
+    /**
+     * Parses a numeric part within a string. Returns the number of characters
+     * parsed.
+     *
+     * @param i index of the first character of the numeric part
+     * @param n lenght of the numeric part
+     * @param date the calendar object where the parsed number is saved
+     * @param field the calendar field where the parsed number is saved
+     * @param adjust an optional adjustment that is added to the parsed number
+     * @return lenght of the numeric part
+     * @throws ValueFormatException if the part is not numeric
+     * @throws IndexOutOfBoundsException if the index is outside the string
+     */
+    private int parse(int i, int n, Calendar date, int field, int adjust)
+            throws ValueFormatException, IndexOutOfBoundsException {
+        int number = 0;
+        for (int j = 0; j < n; j++) {
+            char ch = value.charAt(i + j);
+            if (ch >= '0' && ch <= '9') {
+                number = number * 10 + (ch - '0');
+            } else {
+                throw new ValueFormatException("Invalid value format: " + value);
+            }
+        }
+        date.set(field, number + adjust);
+        return n;
+    }
+
+    /**
+     * Checks that the specified part of the value string is a local name as
+     * specified by JSR 170.
+     *
+     * @param begin begin index of the local name part
+     * @param end endn index of the local name part
+     * @throws ValueFormatException if the specified part is not a local name
+     */
+    private void expectLocalName(int begin, int end)
+            throws ValueFormatException {
+        if (begin >= end) {
+            throw new ValueFormatException("Empty local name: " + value);
+        } else if (!isNonSpace(value.charAt(begin))) {
+            throw new ValueFormatException("Invalid first character: " + value);
+        } else if (!isNonSpace(value.charAt(end - 1))) {
+            throw new ValueFormatException("Invalid last character: " + value);
+        }
+
+        for (int i = begin + 1; i < end - 1; i++) {
+            char ch = value.charAt(i);
+            if (!isNonSpace(ch) && ch != ' ') {
+                throw new ValueFormatException("Invalid name character: " + value);
+            }
+        }
+    }
+
+    /**
+     * Parses the string as a JSR 170 path and returns the name as a committed
+     * value instance.
+     *
+     * @param resolver namespace resolver
+     * @return committed name value
+     * @throws ValueFormatException on name format errors
+     */
+    public Value createName(NamespaceResolver resolver)
+            throws ValueFormatException {
+        if (value.equals(".") || value.equals("..")) {
+            throw new ValueFormatException("Invalid name: " + value);
+        }
+
+        int colon = value.indexOf(':');
+        expectLocalName(colon + 1, value.length());
+        if (colon == -1) {
+            return new CommittedNameValue(value);
+        } else {
+            try {
+                String prefix = value.substring(0, colon);
+                String namespace = resolver.getURI(prefix);
+                return new CommittedPrefixNameValue(
+                        value, prefix, namespace, resolver);
+            } catch (RepositoryException e) {
+                throw new ValueFormatException("Invalid namespace: " + value);
+            }
+        }
+    }
+
+    /**
+     * Checks that the specified part of the value string is a path element as
+     * specified by JSR 170.
+     *
+     * @param begin begin index of the path element
+     * @param end end index of the path element
+     * @param namespaces prefix to namespace URI mappings
+     * @param resolver namespace resolver
+     * @throws ValueFormatException if the specified part is not a path element
+     */
+    private void expectPathElement(
+            int begin, int end, Map namespaces, NamespaceResolver resolver)
+            throws ValueFormatException {
+        if (end == begin) {
+            throw new ValueFormatException("Empty path element: " + value);
+        }
+        if (end == begin + 1 && !isNonSpace(value.charAt(begin))) {
+            throw new ValueFormatException("Invalid path element: " + value);
+        }
+        if (end == begin + 2 && (!isNonSpace(value.charAt(begin))
+                              || !isNonSpace(value.charAt(begin + 1)))) {
+            throw new ValueFormatException("Invalid path element: " + value);
+        }
+
+        // Indexed? "x[1]"
+        if (end >= begin + 4 && value.charAt(end - 1) == ']') {
+            int index = value.lastIndexOf('[', end - 3);
+            if (index <= begin) {
+                throw new ValueFormatException("Invalid path index: " + value);
+            }
+            for (int i = index + 1; i < end - 1; i--) {
+                char ch = value.charAt(i);
+                if (ch < '0' || ch > '9') {
+                    throw new ValueFormatException("Invalid path index: " + value);
+                }
+            }
+            end = index;
+        }
+
+        // Prefixed? "a:b"
+        int colon = value.indexOf(':', begin + 1);
+        if (colon != -1 && colon < end - 1) {
+            String prefix = value.substring(begin, colon);
+            if (!namespaces.containsKey(prefix)) {
+                try {
+                    namespaces.put(prefix, resolver.getURI(prefix));
+                } catch (NamespaceException e) {
+                    throw new ValueFormatException(
+                            "Invalid path prefix: " + value, e);
+                }
+            }
+            begin = colon + 1;
+        }
+
+        expectLocalName(begin, end);
+    }
+
+    /**
+     * Parses the string as a JSR 170 path and returns the path as a committed
+     * value instance.
+     *
+     * @param resolver namespace resolver
+     * @return committed path value
+     * @throws ValueFormatException on path format errors
+     */
+    public Value createPath(NamespaceResolver resolver)
+            throws ValueFormatException {
+        if (value.length() == 0) {
+            throw new ValueFormatException("Empty path");
+        }
+        if (value.equals("/")) {
+            return new CommittedPathValue("/");
+        }
+
+        Map namespaces = new HashMap();
+
+        int position = -1;
+        if (value.charAt(0) == '/') {
+            position = 0;
+        }
+        while (position < value.length()) {
+            int slash = value.indexOf('/', position + 1);
+            if (slash == -1) {
+                slash = value.length();
+            }
+            expectPathElement(position + 1, slash, namespaces, resolver);
+            position = slash;
+        }
+
+        if (namespaces.isEmpty()) {
+            return new CommittedPathValue(value);
+        } else {
+            return new CommittedPrefixPathValue(value, namespaces, resolver);
+        }
+    }
+
+    /**
+     * Returns the string as a boolean value.
+     *
+     * @return boolean value
+     */
+    public boolean getBoolean() {
+        return Boolean.valueOf(value).booleanValue();
+    }
+
+    /**
+     * Parses the string as a ISO8601 date as specified by JSR 170 and returns
+     * the parsed date value.
+     *
+     * @return date value
+     * @throws ValueFormatException on ISO8601 parse errors
+     */
+    public Calendar getDate() throws ValueFormatException {
+        try {
+            long milliseconds;
+            Calendar date = GregorianCalendar.getInstance(UTC);
+            int i = 0;
+
+            char sign = value.charAt(i);
+            if (sign == '-') {
+                date.set(Calendar.ERA, GregorianCalendar.BC);
+            } else {
+                date.set(Calendar.ERA, GregorianCalendar.AD);
+            }
+            if (sign == '-' || sign == '+') {
+                i++;
+            }
+
+            i += parse(i, 4, date, Calendar.YEAR, 0);
+            expect(i++, '-');
+            i += parse(i, 2, date, Calendar.MONTH, -1);
+            expect(i++, '-');
+            i += parse(i, 2, date, Calendar.DAY_OF_MONTH, 0);
+            expect(i++, 'T');
+            i += parse(i, 2, date, Calendar.HOUR_OF_DAY, 0);
+            expect(i++, ':');
+            i += parse(i, 2, date, Calendar.MINUTE, 0);
+            expect(i++, ':');
+            i += parse(i, 2, date, Calendar.SECOND, 0);
+            expect(i++, '.');
+            i += parse(i, 3, date, Calendar.MILLISECOND, 0);
+
+            milliseconds = date.getTimeInMillis();
+            char tz = value.charAt(i++);
+            if (tz == 'Z' && i == value.length()) {
+                // do nothing
+            } else if ((tz == '-' || tz == '+') && i + 5 == value.length()) {
+                i += parse(i, 2, date, Calendar.HOUR_OF_DAY, 0);
+                expect(i++, ':');
+                i += parse(i, 2, date, Calendar.MINUTE, 0);
+                long offset = date.get(Calendar.HOUR_OF_DAY) * 60
+                + date.get(Calendar.MINUTE);
+                if (tz == '-') {
+                    milliseconds += offset * 60 * 1000;
+                } else {
+                    milliseconds -= offset * 60 * 1000;
+                }
+            } else {
+                throw new ValueFormatException("Invalid date format: " + value);
+            }
+
+            date = Calendar.getInstance();
+            date.setTimeInMillis(milliseconds);
+            return date;
+        } catch (IndexOutOfBoundsException e) {
+            throw new ValueFormatException("Invalid date format: " + value, e);
+        }
+    }
+
+    /**
+     * Returns the string as a double value.
+     *
+     * @return double value
+     * @throws ValueFormatException if the string to double conversion fails
+     */
+    public double getDouble() throws ValueFormatException {
+        try {
+            return Double.valueOf(value).doubleValue();
+        } catch (NumberFormatException e) {
+            throw new ValueFormatException("Invalid double value: " + value);
+        }
+    }
+
+    /**
+     * Returns the string as a long value.
+     *
+     * @return long value
+     * @throws ValueFormatException if the string to long conversion fails
+     */
+    public long getLong() throws ValueFormatException {
+        try {
+            return Long.valueOf(value).longValue();
+        } catch (NumberFormatException e) {
+            throw new ValueFormatException("Invalid long value: " + value);
+        }
+    }
+
+    /**
+     * Returns the string as a UTF-8 encoded binary stream.
+     *
+     * @return binary stream
+     * @throws ValueFormatException if the UTF-8 encoding is not available
+     */
+    public InputStream getStream() throws ValueFormatException {
+        try {
+            return new ByteArrayInputStream(value.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new ValueFormatException("UTF-8 not supported", e);
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/ValueParser.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/value/CommittedFalseValue.java
===================================================================
--- src/main/java/org/apache/jackrabbit/value/CommittedFalseValue.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/value/CommittedFalseValue.java	(revision 0)
@@ -0,0 +1,86 @@
+/*
+ * 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.value;
+
+import java.io.Serializable;
+
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+
+/**
+ * Committed boolean <code>false</code> value.
+ */
+class CommittedFalseValue extends CommittedScalarValue implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -4462976650304732295L;
+
+    /**
+     * The singleton instance of this class.
+     */
+    public static final Value INSTANCE = new CommittedFalseValue();
+
+    /**
+     * Private constructor to enforce the singleton instance.
+     */
+    private CommittedFalseValue() {
+    }
+
+    //---------------------------------------------------------------< Value >
+
+    /**
+     * Returns {@link PropertyType#BOOLEAN}.
+     *
+     * @return {@link PropertyType#BOOLEAN}
+     */
+    public int getType() {
+        return PropertyType.BOOLEAN;
+    }
+
+    /**
+     * Returns <code>false</code>.
+     *
+     * @return <code>false</code>
+     */
+    public boolean getBoolean() {
+        return false;
+    }
+
+    /**
+     * Returns "<code>false</code>".
+     *
+     * @return "<code>false</code>"
+     */
+    public String getString() {
+        return Boolean.FALSE.toString();
+    }
+
+    //--------------------------------------------------------< Serializable >
+
+    /**
+     * Enforces the singleton instance of this class by returning it as the
+     * result of deserialization.
+     *
+     * @return {@link #INSTANCE}
+     */
+    private Object readResolve() {
+        return INSTANCE;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/value/CommittedFalseValue.java
___________________________________________________________________
Name: svn:eol-style
   + native

