Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java	(revision 1397066)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java	(working copy)
@@ -23,7 +23,9 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
+
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
@@ -33,12 +35,15 @@
 import javax.jcr.query.QueryManager;
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.qom.QueryObjectModelFactory;
-import org.apache.jackrabbit.oak.api.CoreValue;
+
+import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
-import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.jcr.SessionDelegate;
 import org.apache.jackrabbit.oak.jcr.query.qom.QueryObjectModelFactoryImpl;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * The implementation of the corresponding JCR interface.
@@ -110,7 +115,7 @@
     public QueryResult executeQuery(String statement, String language,
             long limit, long offset, HashMap<String, Value> bindVariableMap) throws RepositoryException {
         try {
-            HashMap<String, CoreValue> bindMap = convertMap(bindVariableMap);
+            Map<String, PropertyValue> bindMap = convertMap(bindVariableMap);
             NamePathMapper namePathMapper = sessionDelegate.getNamePathMapper();
             Result r = queryEngine.executeQuery(statement, language, limit, offset,
                     bindMap, sessionDelegate.getRoot(), namePathMapper);
@@ -122,10 +127,13 @@
         }
     }
 
-    private HashMap<String, CoreValue> convertMap(HashMap<String, Value> bindVariableMap) {
-        HashMap<String, CoreValue> map = new HashMap<String, CoreValue>();
+    private Map<String, PropertyValue> convertMap(
+            HashMap<String, Value> bindVariableMap) throws RepositoryException {
+        HashMap<String, PropertyValue> map = new HashMap<String, PropertyValue>();
         for (Entry<String, Value> e : bindVariableMap.entrySet()) {
-            map.put(e.getKey(), sessionDelegate.getValueFactory().getCoreValue(e.getValue()));
+            map.put(e.getKey(),
+                    PropertyValues.create(PropertyStates.createProperty("",
+                            e.getValue())));
         }
         return map;
     }
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java	(revision 1397066)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java	(working copy)
@@ -30,13 +30,14 @@
 
 import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.RowIteratorAdapter;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.jcr.NodeDelegate;
 import org.apache.jackrabbit.oak.jcr.NodeImpl;
 import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * The implementation of the corresponding JCR interface.
@@ -227,8 +228,8 @@
         };
     }
 
-    Value createValue(CoreValue value) {
-        return value == null ? null : sessionDelegate.getValueFactory().createValue(value);
+    Value createValue(PropertyValue value) {
+        return value == null ? null : sessionDelegate.getValueFactory().createValue(CoreValues.getValue(value));
     }
 
 }
Index: oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java
===================================================================
--- oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java	(revision 1397066)
+++ oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/RowImpl.java	(working copy)
@@ -22,8 +22,9 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.query.Row;
-import org.apache.jackrabbit.oak.api.CoreValue;
+
 import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * The implementation of the corresponding JCR interface.
@@ -89,7 +90,7 @@
 
     @Override
     public Value[] getValues() throws RepositoryException {
-        CoreValue[] values = row.getValues();
+        PropertyValue[] values = row.getValues();
         int len = values.length;
         Value[] v2 = new Value[values.length];
         for (int i = 0; i < len; i++) {
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java	(revision 1397066)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexTest.java	(working copy)
@@ -16,6 +16,10 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.lucene;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import javax.security.auth.Subject;
 
 import org.apache.jackrabbit.mk.core.MicroKernelImpl;
@@ -25,20 +29,16 @@
 import org.apache.jackrabbit.oak.kernel.KernelNodeStore;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinitionImpl;
-import org.apache.jackrabbit.oak.plugins.memory.MemoryValueFactory;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 import org.apache.jackrabbit.oak.security.authorization.AccessControlProviderImpl;
 import org.apache.jackrabbit.oak.spi.query.CompositeQueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.junit.Test;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
 public class LuceneIndexTest implements LuceneIndexConstants {
 
     @Test
@@ -62,7 +62,7 @@
         FilterImpl filter = new FilterImpl(null);
         filter.restrictPath("/", Filter.PathRestriction.EXACT);
         filter.restrictProperty("foo", Operator.EQUAL,
-                MemoryValueFactory.INSTANCE.createValue("bar"));
+                PropertyValues.newString("bar"));
         Cursor cursor = index.query(filter, store.getRoot());
         assertTrue(cursor.next());
         assertEquals("/", cursor.currentRow().getPath());
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java	(revision 1397066)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/query/index/FilterTest.java	(working copy)
@@ -25,10 +25,10 @@
 import java.util.ArrayList;
 import java.util.Random;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
 import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.junit.Test;
 
 /**
@@ -39,8 +39,8 @@
     @Test
     public void propertyRestriction() {
 
-        CoreValue one = new StringValue("1");
-        CoreValue two = new StringValue("2");
+        PropertyValue one = PropertyValues.newString("1");
+        PropertyValue two = PropertyValues.newString("2");
 
         FilterImpl f = new FilterImpl(null);
         assertTrue(null == f.getPropertyRestriction("x"));
@@ -104,8 +104,10 @@
         for (int i = 0; i < 10000; i++) {
             String p1 = paths.get(r.nextInt(paths.size()));
             String p2 = paths.get(r.nextInt(paths.size()));
-            Filter.PathRestriction r1 = Filter.PathRestriction.values()[r.nextInt(Filter.PathRestriction.values().length)];
-            Filter.PathRestriction r2 = Filter.PathRestriction.values()[r.nextInt(Filter.PathRestriction.values().length)];
+            Filter.PathRestriction r1 = Filter.PathRestriction.values()[r
+                    .nextInt(Filter.PathRestriction.values().length)];
+            Filter.PathRestriction r2 = Filter.PathRestriction.values()[r
+                    .nextInt(Filter.PathRestriction.values().length)];
             FilterImpl f1 = new FilterImpl(null);
             f1.restrictPath(p1, r1);
             FilterImpl f2 = new FilterImpl(null);
@@ -123,14 +125,16 @@
                     fc = new FilterImpl(null);
                     fc.restrictPath(p1, r1);
                     fc.restrictPath(p2, r2);
-                    fail("not matched: " + p1 + "/" + r1.name() + " && " + p2 + "/" + r2.name());
+                    fail("not matched: " + p1 + "/" + r1.name() + " && " + p2
+                            + "/" + r2.name());
                 } else {
                     // not great, but not a problem
                     tooMany++;
                 }
             }
             if (tooMany > 3) {
-                fail("too many matches: " + p1 + "/" + r1.name() + " && " + p2 + "/" + r2.name() + " superfluous: " + tooMany);
+                fail("too many matches: " + p1 + "/" + r1.name() + " && " + p2
+                        + "/" + r2.name() + " superfluous: " + tooMany);
             }
         }
     }
@@ -139,7 +143,8 @@
     public void pathRestrictions() throws Exception {
         FilterImpl f = new FilterImpl(null);
         assertEquals("/", f.getPath());
-        assertEquals(Filter.PathRestriction.ALL_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.ALL_CHILDREN,
+                f.getPathRestriction());
 
         f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
         f.restrictPath("/test2", Filter.PathRestriction.ALL_CHILDREN);
@@ -148,10 +153,12 @@
         f = new FilterImpl(null);
         f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
         assertEquals("/test", f.getPath());
-        assertEquals(Filter.PathRestriction.ALL_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.ALL_CHILDREN,
+                f.getPathRestriction());
         f.restrictPath("/test/x", Filter.PathRestriction.DIRECT_CHILDREN);
         assertEquals("/test/x", f.getPath());
-        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN,
+                f.getPathRestriction());
         f.restrictPath("/test/x/y", Filter.PathRestriction.PARENT);
         assertEquals("/test/x/y", f.getPath());
         assertEquals(Filter.PathRestriction.PARENT, f.getPathRestriction());
@@ -212,13 +219,16 @@
         f = new FilterImpl(null);
         f.restrictPath("/test", Filter.PathRestriction.DIRECT_CHILDREN);
         assertEquals("/test", f.getPath());
-        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN,
+                f.getPathRestriction());
         f.restrictPath("/", Filter.PathRestriction.ALL_CHILDREN);
         assertEquals("/test", f.getPath());
-        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN,
+                f.getPathRestriction());
         f.restrictPath("/test", Filter.PathRestriction.ALL_CHILDREN);
         assertEquals("/test", f.getPath());
-        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN, f.getPathRestriction());
+        assertEquals(Filter.PathRestriction.DIRECT_CHILDREN,
+                f.getPathRestriction());
         f.restrictPath("/test/x/y", Filter.PathRestriction.PARENT);
         assertEquals("/test/x/y", f.getPath());
         assertEquals(Filter.PathRestriction.PARENT, f.getPathRestriction());
Index: oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java
===================================================================
--- oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java	(revision 1397066)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/query/AbstractQueryTest.java	(working copy)
@@ -33,19 +33,22 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.AbstractOakTest;
 import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.CoreValueFactory;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -107,7 +110,7 @@
     }
 
     protected Result executeQuery(String statement, String language,
-            HashMap<String, CoreValue> sv) throws ParseException {
+            Map<String, PropertyValue> sv) throws ParseException {
         return qe.executeQuery(statement, language, Long.MAX_VALUE, 0, sv,
                 session.getLatestRoot(), null);
     }
@@ -141,15 +144,15 @@
                 vf);
         root.commit();
 
-        HashMap<String, CoreValue> sv = new HashMap<String, CoreValue>();
-        sv.put("id", vf.createValue("1"));
+        Map<String, PropertyValue> sv = new HashMap<String, PropertyValue>();
+        sv.put("id", PropertyValues.newString("1"));
         Iterator<? extends ResultRow> result;
         result = executeQuery("select * from [nt:base] where id = $id",
                 QueryEngineImpl.SQL2, sv).getRows().iterator();
         assertTrue(result.hasNext());
         assertEquals("/test/hello", result.next().getPath());
 
-        sv.put("id", vf.createValue("2"));
+        sv.put("id", PropertyValues.newString("2"));
         result = executeQuery("select * from [nt:base] where id = $id",
                 QueryEngineImpl.SQL2, sv).getRows().iterator();
         assertTrue(result.hasNext());
@@ -288,13 +291,13 @@
 
     protected static String readRow(ResultRow row) {
         StringBuilder buff = new StringBuilder();
-        CoreValue[] values = row.getValues();
+        PropertyValue[] values = row.getValues();
         for (int i = 0; i < values.length; i++) {
             if (i > 0) {
                 buff.append(", ");
             }
-            CoreValue v = values[i];
-            buff.append(v == null ? "null" : v.getString());
+            PropertyValue v = values[i];
+            buff.append(v == null ? "null" : v.getValue(Type.STRING));
         }
         return buff.toString();
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java	(working copy)
@@ -33,7 +33,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.JcrConstants;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
@@ -42,7 +41,9 @@
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -156,8 +157,8 @@
      * such tree exists or isn't accessible to the content session.
      */
     @CheckForNull
-    public String getPath(CoreValue referenceValue) {
-        int type = referenceValue.getType();
+    public String getPath(PropertyState referenceValue) {
+        int type = referenceValue.getType().tag();
         if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE) {
             return resolveUUID(referenceValue);
         } else {
@@ -188,7 +189,7 @@
                 final String uuid = getIdentifier(tree);
                 String reference = weak ? PropertyType.TYPENAME_WEAKREFERENCE : PropertyType.TYPENAME_REFERENCE;
                 String pName = propertyName == null ? "*" : propertyName;   // TODO: sanitize against injection attacks!?
-                Map<String, ? extends CoreValue> bindings = Collections.singletonMap("uuid", new StringValue(uuid));
+                Map<String, ? extends PropertyValue> bindings = Collections.singletonMap("uuid", PropertyValues.newString(uuid));
 
                 Result result = root.getQueryEngine().executeQuery(
                         "SELECT * FROM [nt:base] WHERE PROPERTY([" + pName + "], '" + reference + "') = $uuid",
@@ -283,12 +284,12 @@
 
     @CheckForNull
     private String resolveUUID(String uuid) {
-        return resolveUUID(new StringValue(uuid));
+        return resolveUUID(PropertyStates.stringProperty("", uuid));
     }
 
-    private String resolveUUID(CoreValue uuid) {
+    private String resolveUUID(PropertyState uuid) {
         try {
-            Map<String, CoreValue> bindings = Collections.singletonMap("id", uuid);
+            Map<String, PropertyValue> bindings = Collections.singletonMap("id", PropertyValues.create(uuid));
             Result result = root.getQueryEngine().executeQuery(
                     "SELECT * FROM [nt:base] WHERE [jcr:uuid] = $id", Query.JCR_SQL2,
                     Long.MAX_VALUE, 0, bindings, root, new NamePathMapper.Default());
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexUpdate.java	(working copy)
@@ -27,7 +27,6 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
@@ -50,22 +49,21 @@
         this.remove = Maps.newHashMap();
     }
 
-    public void insert(String path, Iterable<CoreValue> values) {
+    public void insert(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
-        putValues(insert, path.substring(this.path.length()), values);
+        putValues(insert, path.substring(this.path.length()), value);
     }
 
-    public void remove(String path, Iterable<CoreValue> values) {
+    public void remove(String path, PropertyState value) {
         Preconditions.checkArgument(path.startsWith(this.path));
-        putValues(remove, path.substring(this.path.length()), values);
+        putValues(remove, path.substring(this.path.length()), value);
     }
 
-    private static void putValues(
-            Map<String, Set<String>> map,
-            String path, Iterable<CoreValue> values) {
-        for (CoreValue value : values) {
-            if (value.getType() != PropertyType.BINARY) {
-                String key = PropertyIndex.encode(value);
+    private static void putValues(Map<String, Set<String>> map, String path,
+            PropertyState value) {
+        if (value.getType().tag() != PropertyType.BINARY) {
+            List<String> keys = PropertyIndex.encode(value);
+            for (String key : keys) {
                 Set<String> paths = map.get(key);
                 if (paths == null) {
                     paths = Sets.newHashSet();
@@ -76,7 +74,6 @@
         }
     }
 
-
     public void apply() throws CommitFailedException {
         boolean unique = node.getProperty("unique") != null;
         NodeBuilder index = node.child(":index");
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexDiff.java	(working copy)
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -109,22 +108,22 @@
     @Override
     public void propertyAdded(PropertyState after) {
         for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.insert(getPath(), CoreValues.getValues(after));
+            update.insert(getPath(), after);
         }
     }
 
     @Override
     public void propertyChanged(PropertyState before, PropertyState after) {
         for (PropertyIndexUpdate update : getIndexes(after.getName())) {
-            update.remove(getPath(), CoreValues.getValues(before));
-            update.insert(getPath(), CoreValues.getValues(after));
+            update.remove(getPath(), before);
+            update.insert(getPath(), after);
         }
     }
 
     @Override
     public void propertyDeleted(PropertyState before) {
         for (PropertyIndexUpdate update : getIndexes(before.getName())) {
-            update.remove(getPath(), CoreValues.getValues(before));
+            update.remove(getPath(), before);
         }
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java	(working copy)
@@ -18,11 +18,10 @@
 
 import java.util.Set;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.plugins.memory.StringValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -66,10 +65,10 @@
     }
 
     public Set<String> find(String name, String value) {
-        return find(name, new StringValue(value));
+        return find(name, PropertyValues.newString(value));
     }
 
-    public Set<String> find(String name, CoreValue value) {
+    public Set<String> find(String name, PropertyValue value) {
         Set<String> paths = Sets.newHashSet();
 
         PropertyState property = null;
@@ -79,7 +78,8 @@
             if (state != null) {
                 state = state.getChildNode(":index");
                 if (state != null) {
-                    property = state.getProperty(PropertyIndex.encode(value));
+                    //TODO what happens when I search using an mvp?
+                    property = state.getProperty(PropertyIndex.encode(value).get(0));
                 }
             }
         }
@@ -92,12 +92,9 @@
         } else {
             // No index available, so first check this node for a match
             property = root.getProperty(name);
-            if (property != null) {
-                for (CoreValue cv : CoreValues.getValues(property)) {
-                    if (cv.equals(value)) {
-                        paths.add("");
-                        break;
-                    }
+            if (property != null){
+                if(PropertyValues.match(property, value)){
+                    paths.add("");
                 }
             }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndex.java	(working copy)
@@ -18,17 +18,20 @@
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 import org.apache.jackrabbit.oak.query.index.TraversingCursor;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
-import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -39,16 +42,20 @@
 
     private static final int MAX_STRING_LENGTH = 100; // TODO: configurable
 
-    static String encode(CoreValue value) {
-        try {
-            String string = value.getString();
-            if (string.length() > MAX_STRING_LENGTH) {
-                string.substring(0, MAX_STRING_LENGTH);
+    static List<String> encode(PropertyState value) {
+        List<String> values = new ArrayList<String>();
+
+        for (String v : value.getValue(Type.STRINGS)) {
+            try {
+                if (v.length() > MAX_STRING_LENGTH) {
+                    v = v.substring(0, MAX_STRING_LENGTH);
+                }
+                values.add(URLEncoder.encode(v, Charsets.UTF_8.name()));
+            } catch (UnsupportedEncodingException e) {
+                throw new IllegalStateException("UTF-8 is unsupported", e);
             }
-            return URLEncoder.encode(string, Charsets.UTF_8.name());
-        } catch (UnsupportedEncodingException e) {
-           throw new IllegalStateException("UTF-8 is unsupported", e);
         }
+        return values;
     }
 
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneEditor.java	(working copy)
@@ -26,11 +26,11 @@
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -220,28 +220,30 @@
             document.add(newPathField(path));
             for (PropertyState property : state.getProperties()) {
                 String pname = property.getName();
-                for (CoreValue value : CoreValues.getValues(property)) {
-                    document.add(newPropertyField(pname,
-                            parseStringValue(value)));
+                switch (property.getType().tag()) {
+                case PropertyType.BINARY:
+                    for (Blob v : property.getValue(Type.BINARIES)) {
+                        document.add(newPropertyField(pname,
+                                parseStringValue(v)));
+                    }
+                    break;
+                default:
+                    for (String v : property.getValue(Type.STRINGS)) {
+                        document.add(newPropertyField(pname, v));
+                    }
+                    break;
                 }
             }
             return document;
         }
 
-        private static String parseStringValue(CoreValue value) {
-            String string;
-            if (value.getType() != PropertyType.BINARY) {
-                string = value.getString();
-            } else {
-                try {
-                    string = TIKA.parseToString(value.getNewStream());
-                } catch (IOException e) {
-                    string = "";
-                } catch (TikaException e) {
-                    string = "";
-                }
+        private static String parseStringValue(Blob v) {
+            try {
+                return TIKA.parseToString(v.getNewStream());
+            } catch (IOException e) {
+            } catch (TikaException e) {
             }
-            return string;
+            return "";
         }
 
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndex.java	(working copy)
@@ -29,6 +29,7 @@
 import javax.jcr.nodetype.NodeTypeManager;
 
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.core.ReadOnlyTree;
 import org.apache.jackrabbit.oak.plugins.index.IndexDefinition;
@@ -200,10 +201,10 @@
             boolean isLike = pr.isLike;
 
             if (pr.first != null) {
-                first = pr.first.getString();
+                first = pr.first.getValue(Type.STRING);
             }
             if (pr.last != null) {
-                last = pr.last.getString();
+                last = pr.last.getValue(Type.STRING);
             }
 
             if (isLike) {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PrefixContentIndex.java	(working copy)
@@ -22,6 +22,7 @@
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.kernel.TypeCodes;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
@@ -80,7 +81,7 @@
             throw new IllegalArgumentException("No restriction for *");
         }
         // TODO need to use correct json representation
-        String v = restriction.first.getString();
+        String v = restriction.first.getValue(Type.STRING);
         v = index.getPrefix() + v;
         return "prefixIndex \"" + v + '"';
     }
@@ -92,7 +93,7 @@
             throw new IllegalArgumentException("No restriction for *");
         }
         // TODO need to use correct json representation
-        String v = restriction.first.getString();
+        String v = restriction.first.getValue(Type.STRING);
         v = index.getPrefix() + v;
         // TODO revisit code after the removal of revisionId
         String revisionId = "";
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/old/PropertyContentIndex.java	(working copy)
@@ -19,7 +19,9 @@
 package org.apache.jackrabbit.oak.plugins.index.old;
 
 import java.util.Iterator;
-import org.apache.jackrabbit.oak.api.CoreValue;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.index.IndexRowImpl;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
@@ -69,8 +71,8 @@
         if (restriction == null) {
             throw new IllegalArgumentException("No restriction for " + propertyName);
         }
-        CoreValue first = restriction.first;
-        String f = first == null ? null : first.toString();
+        PropertyState first = restriction.first;
+        String f = first == null ? null : first.getValue(Type.STRING);
         // TODO revisit code after the removal of revisionId
         String revisionId = "";
         Iterator<String> it = index.getPaths(f, revisionId);
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/StringBasedBlob.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/StringBasedBlob.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/StringBasedBlob.java	(working copy)
@@ -31,6 +31,11 @@
         this.value = value;
     }
 
+    @Override
+    public String toString() {
+        return value;
+    }
+
     @Nonnull
     @Override
     public InputStream getNewStream() {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ArrayBasedBlob.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ArrayBasedBlob.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ArrayBasedBlob.java	(working copy)
@@ -20,6 +20,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 
 import javax.annotation.Nonnull;
 
@@ -30,6 +31,15 @@
         this.value = value;
     }
 
+    @Override
+    public String toString() {
+        try {
+            return new String(value, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            return "<blob>";
+        }
+    }
+
     @Nonnull
     @Override
     public InputStream getNewStream() {
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/BinaryPropertyState.java	(working copy)
@@ -29,7 +29,8 @@
 
     @Override
     public String getString() {
-        return "<binary>";
+        // TODO find a better way to represent string values with Blobs
+        return value.toString();
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/SessionQueryEngineImpl.java	(working copy)
@@ -20,11 +20,11 @@
 import java.util.List;
 import java.util.Map;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.SessionQueryEngine;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
@@ -53,7 +53,7 @@
 
     @Override
     public Result executeQuery(String statement, String language, long limit,
-            long offset, Map<String, ? extends CoreValue> bindings, Root root,
+            long offset, Map<String, ? extends PropertyValue> bindings, Root root,
             NamePathMapper namePathMapper) throws ParseException {
         return queryEngine.executeQuery(statement, language, limit, offset,
                 bindings, root, namePathMapper);
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java	(working copy)
@@ -13,8 +13,6 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -22,15 +20,9 @@
 import java.util.Iterator;
 import java.util.List;
 
-import javax.jcr.PropertyType;
-
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.CoreValueFactory;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.query.ast.AstVisitorBase;
 import org.apache.jackrabbit.oak.query.ast.BindVariableValueImpl;
 import org.apache.jackrabbit.oak.query.ast.ChildNodeImpl;
@@ -57,6 +49,8 @@
 import org.apache.jackrabbit.oak.query.ast.SourceImpl;
 import org.apache.jackrabbit.oak.query.ast.UpperCaseImpl;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -80,7 +74,7 @@
 
     final SourceImpl source;
     final ConstraintImpl constraint;
-    final HashMap<String, CoreValue> bindVariableMap = new HashMap<String, CoreValue>();
+    final HashMap<String, PropertyValue> bindVariableMap = new HashMap<String, PropertyValue>();
     final HashMap<String, Integer> selectorIndexes = new HashMap<String, Integer>();
     final ArrayList<SelectorImpl> selectors = new ArrayList<SelectorImpl>();
 
@@ -92,17 +86,15 @@
     private long offset;
     private long size = -1;
     private boolean prepared;
-    private final CoreValueFactory valueFactory;
     private Root root;
     private NamePathMapper namePathMapper;
 
     Query(SourceImpl source, ConstraintImpl constraint, OrderingImpl[] orderings,
-          ColumnImpl[] columns, CoreValueFactory valueFactory) {
+          ColumnImpl[] columns) {
         this.source = source;
         this.constraint = constraint;
         this.orderings = orderings;
         this.columns = columns;
-        this.valueFactory = valueFactory;
     }
 
     public void init() {
@@ -280,7 +272,7 @@
         return source;
     }
 
-    void bindValue(String varName, CoreValue value) {
+    void bindValue(String varName, PropertyValue value) {
         bindVariableMap.put(varName, value);
     }
 
@@ -292,10 +284,6 @@
         this.offset = offset;
     }
 
-    public CoreValueFactory getValueFactory() {
-        return valueFactory;
-    }
-
     public void setExplain(boolean explain) {
         this.explain = explain;
     }
@@ -315,8 +303,8 @@
             String plan = source.getPlan(root);
             columns = new ColumnImpl[] { new ColumnImpl("explain", "plan", "plan")};
             ResultRowImpl r = new ResultRowImpl(this,
-                    new String[0],
-                    new CoreValue[] { getValueFactory().createValue(plan) },
+                    new String[0], 
+                    new PropertyValue[] { PropertyValues.newString(plan)},
                     null);
             it = Arrays.asList(r).iterator();
         } else {
@@ -347,18 +335,18 @@
                 ArrayList<ResultRowImpl> list = new ArrayList<ResultRowImpl>();
                 ResultRowImpl r = new ResultRowImpl(this,
                         new String[0],
-                        new CoreValue[] {
-                            getValueFactory().createValue("query"),
-                            getValueFactory().createValue(resultCount),
+                        new PropertyValue[] {
+                                PropertyValues.newString("query"),
+                                PropertyValues.newLong(resultCount)
                             },
                         null);
                 list.add(r);
                 for (SelectorImpl selector : selectors) {
                     r = new ResultRowImpl(this,
                             new String[0],
-                            new CoreValue[] {
-                                getValueFactory().createValue(selector.getSelectorName()),
-                                getValueFactory().createValue(selector.getScanCount()),
+                            new PropertyValue[] {
+                                    PropertyValues.newString(selector.getSelectorName()),
+                                    PropertyValues.newLong(selector.getScanCount()),
                                 },
                             null);
                     list.add(r);
@@ -369,11 +357,12 @@
         return it;
     }
 
-    public int compareRows(CoreValue[][] orderValues, CoreValue[][] orderValues2) {
+    public int compareRows(PropertyValue[] orderValues,
+            PropertyValue[] orderValues2) {
         int comp = 0;
         for (int i = 0, size = orderings.length; i < size; i++) {
-            CoreValue[] a = orderValues[i];
-            CoreValue[] b = orderValues2[i];
+            PropertyValue a = orderValues[i];
+            PropertyValue b = orderValues2[i];
             if (a == null || b == null) {
                 if (a == b) {
                     comp = 0;
@@ -384,7 +373,7 @@
                     comp = 1;
                 }
             } else {
-                comp = compareValues(a, b);
+                comp = a.compareTo(b);
             }
             if (comp != 0) {
                 if (orderings[i].isDescending()) {
@@ -396,125 +385,6 @@
         return comp;
     }
 
-    public static int compareValues(CoreValue[] orderValues, CoreValue[] orderValues2) {
-        int l1 = orderValues.length;
-        int l2 = orderValues2.length;
-        int len = Math.max(l1, l2);
-        for (int i = 0; i < len; i++) {
-            CoreValue a = i < l1 ? orderValues[i] : null;
-            CoreValue b = i < l2 ? orderValues2[i] : null;
-            int comp;
-            if (a == null) {
-                comp = 1;
-            } else if (b == null) {
-                comp = -1;
-            } else {
-                comp = a.compareTo(b);
-            }
-            if (comp != 0) {
-                return comp;
-            }
-        }
-        return 0;
-    }
-
-    public static int getType(PropertyState p, int ifUnknown) {
-        if (p.count() > 0) {
-            return p.getType().tag();
-        }
-        return ifUnknown;
-    }
-
-    /**
-     * Convert a value to the given target type, if possible.
-     * 
-     * @param v the value to convert
-     * @param targetType the target property type
-     * @return the converted value, or null if converting is not possible
-     */
-    public CoreValue convert(CoreValue v, int targetType) {
-        // TODO support full set of conversion features defined in the JCR spec
-        // at 3.6.4 Property Type Conversion
-        // re-use existing code if possible
-        int sourceType = v.getType();
-        if (sourceType == targetType) {
-            return v;
-        }
-        CoreValueFactory vf = getValueFactory();
-        switch (sourceType) {
-        case PropertyType.STRING:
-            switch(targetType) {
-            case PropertyType.BINARY:
-                try {
-                    byte[] data = v.getString().getBytes("UTF-8");
-                    return vf.createValue(new ByteArrayInputStream(data));
-                } catch (IOException e) {
-                    // I don't know in what case that could really occur
-                    // except if UTF-8 isn't supported
-                    throw new IllegalArgumentException(v.getString(), e);
-                }
-            }
-            return vf.createValue(v.getString(), targetType);
-        }
-        try {
-            switch (targetType) {
-            case PropertyType.STRING:
-                return vf.createValue(v.getString());
-            case PropertyType.BOOLEAN:
-                return vf.createValue(v.getBoolean());
-            case PropertyType.DATE:
-                return vf.createValue(v.getString(), PropertyType.DATE);
-            case PropertyType.LONG:
-                return vf.createValue(v.getLong());
-            case PropertyType.DOUBLE:
-                return vf.createValue(v.getDouble());
-            case PropertyType.DECIMAL:
-                return vf.createValue(v.getString(), PropertyType.DECIMAL);
-            case PropertyType.NAME:
-                return vf.createValue(getOakPath(v.getString()), PropertyType.NAME);
-            case PropertyType.PATH:
-                return vf.createValue(v.getString(), PropertyType.PATH);
-            case PropertyType.REFERENCE:
-                return vf.createValue(v.getString(), PropertyType.REFERENCE);
-            case PropertyType.WEAKREFERENCE:
-                return vf.createValue(v.getString(), PropertyType.WEAKREFERENCE);
-            case PropertyType.URI:
-                return vf.createValue(v.getString(), PropertyType.URI);
-            case PropertyType.BINARY:
-                try {
-                    byte[] data = v.getString().getBytes("UTF-8");
-                    return vf.createValue(new ByteArrayInputStream(data));
-                } catch (IOException e) {
-                    // I don't know in what case that could really occur
-                    // except if UTF-8 isn't supported
-                    throw new IllegalArgumentException(v.getString(), e);
-                }
-            }
-            throw new IllegalArgumentException("Unknown property type: " + targetType);
-        } catch (UnsupportedOperationException e) {
-            // TODO detect unsupported conversions, so that no exception is thrown
-            // because exceptions are slow
-            return null;
-            // throw new IllegalArgumentException("<unsupported conversion of " + 
-            //        v + " (" + PropertyType.nameFromValue(v.getType()) + ") to type " + 
-            //        PropertyType.nameFromValue(targetType) + ">");
-        }
-    }
-
-    public String getOakPath(String jcrPath) {
-        NamePathMapper m = getNamePathMapper();
-        if (m == null) {
-            // to simplify testing, a getNamePathMapper isn't required
-            return jcrPath;
-        }
-        String p = m.getOakPath(jcrPath);
-        if (p == null) {
-            throw new IllegalArgumentException("Not a valid JCR path: "
-                    + jcrPath);
-        }
-        return p;
-    }
-
     void prepare() {
         if (prepared) {
             return;
@@ -609,30 +479,19 @@
             paths[i] = s.currentPath();
         }
         int columnCount = columns.length;
-        CoreValue[] values = new CoreValue[columnCount];
+        PropertyValue[] values = new PropertyValue[columnCount];
         for (int i = 0; i < columnCount; i++) {
             ColumnImpl c = columns[i];
-            PropertyState p = c.currentProperty();
-            values[i] = p == null ? null : CoreValues.getValue(p);
+            values[i] = PropertyValues.create(c.currentProperty());
         }
-        CoreValue[][] orderValues;
+        PropertyValue[] orderValues;
         if (orderings == null) {
             orderValues = null;
         } else {
             int size = orderings.length;
-            orderValues = new CoreValue[size][];
+            orderValues = new PropertyValue[size];
             for (int i = 0; i < size; i++) {
-                PropertyState p = orderings[i].getOperand().currentProperty();
-                CoreValue[] x;
-                if (p == null) {
-                    x = null;
-                } else if (p.isArray()) {
-                    List<CoreValue> list = CoreValues.getValues(p);
-                    x = list.toArray(new CoreValue[list.size()]);
-                } else {
-                    x = new CoreValue[] { CoreValues.getValue(p) };
-                }
-                orderValues[i] = x;
+                orderValues[i] = PropertyValues.create(orderings[i].getOperand().currentProperty());
             }
         }
         return new ResultRowImpl(this, paths, values, orderValues);
@@ -657,8 +516,8 @@
         throw new IllegalArgumentException("Column not found: " + columnName);
     }
 
-    public CoreValue getBindVariableValue(String bindVariableName) {
-        CoreValue v = bindVariableMap.get(bindVariableName);
+    public PropertyValue getBindVariableValue(String bindVariableName) {
+        PropertyValue v = bindVariableMap.get(bindVariableName);
         if (v == null) {
             throw new IllegalArgumentException("Bind variable value not set: " + bindVariableName);
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java	(working copy)
@@ -18,6 +18,8 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+
 import java.util.Set;
 
 import javax.annotation.CheckForNull;
@@ -26,10 +28,8 @@
 import javax.jcr.nodetype.NodeTypeIterator;
 import javax.jcr.nodetype.NodeTypeManager;
 
-import com.google.common.collect.ImmutableSet;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
 import org.apache.jackrabbit.oak.query.Query;
@@ -37,10 +37,12 @@
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
-import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * A selector within a query.
@@ -216,7 +218,7 @@
         return cursor == null ? null : cursor.currentRow().getPath();
     }
 
-    public PropertyState currentProperty(String propertyName) {
+    public PropertyValue currentProperty(String propertyName) {
         if (propertyName.equals(Query.JCR_PATH)) {
             String p = currentPath();
             if (p == null) {
@@ -227,7 +229,7 @@
                 // not a local path
                 return null;
             }
-            return PropertyStates.stringProperty(Query.JCR_PATH, local);
+            return PropertyValues.newString(local);
         }
         if (cursor == null) {
             return null;
@@ -243,7 +245,7 @@
             return null;
         }
         Tree t = getTree(path);
-        return t == null ? null : t.getProperty(propertyName);
+        return t == null ? null : PropertyValues.create(t.getProperty(propertyName));
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyValueImpl.java	(working copy)
@@ -24,16 +24,17 @@
 
 import javax.jcr.PropertyType;
 
-import com.google.common.collect.Iterables;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.SQL2Parser;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
+import com.google.common.collect.Iterables;
+
 /**
  * A property expression.
  */
@@ -81,11 +82,11 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         boolean relative = propertyName.indexOf('/') >= 0;
         boolean asterisk = propertyName.equals("*");
         if (!relative && !asterisk) {
-            PropertyState p = selector.currentProperty(propertyName);
+            PropertyValue p = selector.currentProperty(propertyName);
             return matchesPropertyType(p) ? p : null;
         }
         Tree tree = getTree(selector.currentPath());
@@ -112,7 +113,7 @@
                 return null;
             }
             PropertyState p = tree.getProperty(name);
-            return matchesPropertyType(p) ? p : null;
+            return matchesPropertyType(p) ? PropertyValues.create(p) : null;
         }
         // asterisk - create a multi-value property
         // warning: the returned property state may have a mixed type
@@ -125,7 +126,8 @@
                 Iterables.addAll(values, p.getValue(Type.STRINGS));
             }
         }
-        return PropertyStates.stringProperty("*", values);
+        // "*"
+        return PropertyValues.newString(values);
     }
 
     private boolean matchesPropertyType(PropertyState state) {
@@ -143,7 +145,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         if (f.getSelector() == selector) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeNameImpl.java	(working copy)
@@ -20,11 +20,11 @@
 
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.util.ISO9075;
 
 /**
@@ -59,22 +59,22 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         String path = selector.currentPath();
         // Name escaping (convert space to _x0020_)
         String name = ISO9075.encode(PathUtils.getName(path));
-        return PropertyStates.nameProperty("NAME", name);
+        return PropertyValues.newName(name);
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         if (v == null) {
             return;
         }
         if (!isName(v)) {
             throw new IllegalArgumentException("Invalid name value: " + v.toString());
         }
-        String path = v.getString();
+        String path = v.getValue(Type.STRING);
         // Name escaping (convert _x0020_ to space)
         path = decodeName(path);
         if (PathUtils.isAbsolute(path)) {
@@ -95,7 +95,7 @@
         // Name escaping (convert _x0020_ to space)
         path = ISO9075.decode(path);
         // normalize paths (./name > name)
-        path = query.getOakPath(path);
+        path = PropertyValues.getOakPath(path, query.getNamePathMapper());
         return path;
     }
 
@@ -105,9 +105,9 @@
      * @param v the value
      * @return true if it can be converted
      */
-    private static boolean isName(CoreValue v) {
+    private static boolean isName(PropertyValue v) {
         // TODO correctly validate JCR names - see JCR 2.0 spec 3.2.4 Naming Restrictions
-        switch (v.getType()) {
+        switch (v.getType().tag()) {
         case PropertyType.DATE:
         case PropertyType.DECIMAL:
         case PropertyType.DOUBLE:
@@ -115,7 +115,7 @@
         case PropertyType.BOOLEAN:
             return false;
         }
-        String n = v.getString();
+        String n = v.getValue(Type.STRING);
         if (n.startsWith("[") && !n.endsWith("]")) {
             return false;
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchScoreImpl.java	(working copy)
@@ -18,11 +18,10 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.Query;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * A fulltext search score expression.
@@ -47,11 +46,11 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
-        PropertyState p = selector.currentProperty(Query.JCR_SCORE);
+    public PropertyValue currentProperty() {
+        PropertyValue p = selector.currentProperty(Query.JCR_SCORE);
         if (p == null) {
             // TODO if score() is not supported by the index, use the value 0.0?
-            return PropertyStates.doubleProperty(Query.JCR_SCORE, 0.0);
+            return PropertyValues.newDouble(0.0);
         }
         return p;
     }
@@ -61,7 +60,7 @@
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         if (f.getSelector() == selector) {
             if (operator == Operator.NOT_EQUAL && v != null) {
                 // not supported
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/EquiJoinConditionImpl.java	(working copy)
@@ -18,11 +18,9 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.query.Query;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * The "a.x = b.y" join condition.
@@ -62,61 +60,53 @@
 
     @Override
     public boolean evaluate() {
-        PropertyState p1 = selector1.currentProperty(property1Name);
+        PropertyValue p1 = selector1.currentProperty(property1Name);
         if (p1 == null) {
             return false;
         }
-        PropertyState p2 = selector2.currentProperty(property2Name);
+        PropertyValue p2 = selector2.currentProperty(property2Name);
         if (p2 == null) {
             return false;
         }
         if (!p1.isArray() && !p2.isArray()) {
             // both are single valued
-            return CoreValues.getValue(p1).equals(CoreValues.getValue(p2));
+            return PropertyValues.match(p1, p2);
         }
         // TODO what is the expected result of an equi join for multi-valued properties?
         if (!p1.isArray() && p2.isArray()) {
-            CoreValue x = CoreValues.getValue(p1);
-            for (CoreValue y : CoreValues.getValues(p2)) {
-                if (y.getType() != x.getType()) {
-                    y = query.convert(y, x.getType());
-                }
-                if (y != null && x.equals(y)) {
-                    return true;
-                }
+            if (p1.getType().tag() != p2.getType().tag()) {
+                p1 = PropertyValues.convert(p1, p2.getType().tag(), query.getNamePathMapper());
             }
+            if (p1 != null && PropertyValues.match(p1, p2)) {
+                return true;
+            }
             return false;
         } else if (p1.isArray() && !p2.isArray()) {
-            CoreValue x = CoreValues.getValue(p2);
-            for (CoreValue y : CoreValues.getValues(p1)) {
-                if (y.getType() != x.getType()) {
-                    y = query.convert(y, x.getType());
-                }
-                if (x.equals(y)) {
-                    return true;
-                }
+            if (p1.getType().tag() != p2.getType().tag()) {
+                p2 = PropertyValues.convert(p2, p1.getType().tag(), query.getNamePathMapper());
             }
+            if (p2 != null && PropertyValues.match(p1, p2)) {
+                return true;
+            }
             return false;
         }
-        CoreValue[] l1 = CoreValues.getValues(p1).toArray(new CoreValue[p1.count()]);
-        CoreValue[] l2 = CoreValues.getValues(p2).toArray(new CoreValue[p2.count()]);
-        return Query.compareValues(l1, l2) == 0;
+        return PropertyValues.match(p1, p2);
     }
 
     @Override
     public void restrict(FilterImpl f) {
-        PropertyState p1 = selector1.currentProperty(property1Name);
-        PropertyState p2 = selector2.currentProperty(property2Name);
+        PropertyValue p1 = selector1.currentProperty(property1Name);
+        PropertyValue p2 = selector2.currentProperty(property2Name);
         if (f.getSelector() == selector1 && p2 != null) {
             if (!p2.isArray()) {
                 // TODO support join on multi-valued properties
-                f.restrictProperty(property1Name, Operator.EQUAL, CoreValues.getValue(p2));
+                f.restrictProperty(property1Name, Operator.EQUAL, p2);
             }
         }
         if (f.getSelector() == selector2 && p1 != null) {
             if (!p1.isArray()) {
                 // TODO support join on multi-valued properties
-                f.restrictProperty(property2Name, Operator.EQUAL, CoreValues.getValue(p1));
+                f.restrictProperty(property2Name, Operator.EQUAL, p1);
             }
         }
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LiteralImpl.java	(working copy)
@@ -19,23 +19,25 @@
 package org.apache.jackrabbit.oak.query.ast;
 
 import java.util.Locale;
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.query.SQL2Parser;
 
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.query.SQL2Parser;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+
 /**
  * A literal of a certain data type, possibly "cast(..)" of a literal.
  */
 public class LiteralImpl extends StaticOperandImpl {
 
-    private final CoreValue value;
+    private final PropertyValue value;
 
-    public LiteralImpl(CoreValue value) {
+    public LiteralImpl(PropertyValue value) {
         this.value = value;
     }
 
-    public CoreValue getLiteralValue() {
+    public PropertyValue getLiteralValue() {
         return value;
     }
 
@@ -46,16 +48,16 @@
 
     @Override
     public String toString() {
-        String type = PropertyType.nameFromValue(value.getType());
+        String type = PropertyType.nameFromValue(value.getType().tag());
         return "cast(" + escape() + " as " + type.toLowerCase(Locale.ENGLISH) + ')';
     }
 
     private String escape() {
-        return SQL2Parser.escapeStringLiteral(value.getString());
+        return SQL2Parser.escapeStringLiteral(value.getValue(Type.STRING));
     }
 
     @Override
-    CoreValue currentValue() {
+    PropertyValue currentValue() {
         return value;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/DynamicOperandImpl.java	(working copy)
@@ -16,18 +16,17 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * The base class for dynamic operands (such as a function or property).
  */
 public abstract class DynamicOperandImpl extends AstElement {
 
-    public abstract PropertyState currentProperty();
+    public abstract PropertyValue currentProperty();
 
-    public abstract void restrict(FilterImpl f, Operator operator, CoreValue v);
+    public abstract void restrict(FilterImpl f, Operator operator, PropertyValue v);
 
     /**
      * Check whether the condition can be applied to a selector (to restrict the
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/NodeLocalNameImpl.java	(working copy)
@@ -18,11 +18,10 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.util.ISO9075;
 
 /**
@@ -52,18 +51,18 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         String name = PathUtils.getName(selector.currentPath());
         // Name escaping (convert space to _x0020_)
         name = ISO9075.encode(name);
         int colon = name.indexOf(':');
         // TODO LOCALNAME: evaluation of local name might not be correct
         String localName = colon < 0 ? name : name.substring(colon + 1);
-        return PropertyStates.stringProperty("LOCALNAME", localName);
+        return PropertyValues.newString(localName);
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         // TODO support LOCALNAME index conditions
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ComparisonImpl.java	(working copy)
@@ -18,11 +18,10 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
-import org.apache.jackrabbit.oak.query.Query;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * A comparison operation (including "like").
@@ -51,67 +50,76 @@
         return operand2;
     }
 
+    public static int getType(PropertyValue p, int ifUnknown) {
+        if (p.count() > 0) {
+            return p.getType().tag();
+        }
+        return ifUnknown;
+    }
+
     @Override
     public boolean evaluate() {
         // JCR 2.0 spec, 6.7.16 Comparison:
         // "operand1 may evaluate to an array of values"
-        PropertyState p1 = operand1.currentProperty();
+        PropertyValue p1 = operand1.currentProperty();
         if (p1 == null) {
             return false;
         }
-        // "operand2 always evaluates to a scalar value"
-        CoreValue v2 = operand2.currentValue();
-        if (v2 == null) {
+        PropertyValue p2 = operand2.currentValue();
+        if (p2 == null) {
             // if the property doesn't exist, the result is always false
             // even for "null <> 'x'" (same as in SQL) 
             return false;
         }
-        boolean isArray = p1.isArray();
-        int v1Type = Query.getType(p1, v2.getType());
-        if (v1Type != v2.getType()) {
+        int v1Type = getType(p1, p2.getType().tag());
+        if (v1Type != p2.getType().tag()) {
             // "the value of operand2 is converted to the
             // property type of the value of operand1"
-            v2 = query.convert(v2, v1Type);
-            if (v2 == null) {
+            p2 = PropertyValues.convert(p2, v1Type, query.getNamePathMapper());
+            if (p2 == null) {
                 return false;
             }
         }
-        if (!isArray) {
-            return evaluate(CoreValues.getValue(p1), v2);
-        }
-        // for multi-valued properties: if any of the value matches,
-        // then return true
-        for (CoreValue v1 : CoreValues.getValues(p1)) {
-            if (evaluate(v1, v2)) {
-                return true;
-            }
-        }
-        return false;
+        return evaluate(p1, p2);
     }
 
-    private boolean evaluate(CoreValue v1, CoreValue v2) {
+    /**
+     * "operand2 always evaluates to a scalar value"
+     * 
+     * for multi-valued properties: if any of the value matches, then return true
+     * 
+     * @param p1
+     * @param p2
+     * @return
+     */
+    private boolean evaluate(PropertyValue p1, PropertyValue p2) {
         switch (operator) {
         case EQUAL:
-            return v1.equals(v2);
+            return PropertyValues.match(p1, p2);
+        case NOT_EQUAL:
+            return !PropertyValues.match(p1, p2);
         case GREATER_OR_EQUAL:
-            return v1.compareTo(v2) >= 0;
+            return p1.compareTo(p2) >= 0;
         case GREATER_THAN:
-            return v1.compareTo(v2) > 0;
+            return p1.compareTo(p2) > 0;
         case LESS_OR_EQUAL:
-            return v1.compareTo(v2) <= 0;
+            return p1.compareTo(p2) <= 0;
         case LESS_THAN:
-            return v1.compareTo(v2) < 0;
-        case NOT_EQUAL:
-            return !v1.equals(v2);
+            return p1.compareTo(p2) < 0;
         case LIKE:
-            return evaluateLike(v1, v2);
+            return evaluateLike(p1, p2);
         }
         throw new IllegalArgumentException("Unknown operator: " + operator);
     }
 
-    private static boolean evaluateLike(CoreValue v1, CoreValue v2) {
-        LikePattern like = new LikePattern(v2.getString());
-        return like.matches(v1.getString());
+    private static boolean evaluateLike(PropertyValue v1, PropertyValue v2) {
+        LikePattern like = new LikePattern(v2.getValue(Type.STRING));
+        for (String s : v1.getValue(Type.STRINGS)) {
+            if (like.matches(s)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -126,7 +134,7 @@
 
     @Override
     public void restrict(FilterImpl f) {
-        CoreValue v = operand2.currentValue();
+        PropertyValue v = operand2.currentValue();
         if (v != null) {
             operand1.restrict(f, operator, v);
             // TODO OAK-347
@@ -155,8 +163,7 @@
 
     @Override
     public void restrictPushDown(SelectorImpl s) {
-        CoreValue v = operand2.currentValue();
-        if (v != null) {
+        if (operand2.currentValue() != null) {
             if (operand1.canRestrictSelector(s)) {
                 s.restrictSelector(this);
             }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/BindVariableValueImpl.java	(working copy)
@@ -18,7 +18,7 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * A bind variable.
@@ -46,7 +46,7 @@
     }
 
     @Override
-    CoreValue currentValue() {
+    PropertyValue currentValue() {
         return query.getBindVariableValue(bindVariableName);
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LengthImpl.java	(working copy)
@@ -20,10 +20,10 @@
 
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * The function "length(..)".
@@ -51,23 +51,23 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         PropertyState p = propertyValue.currentProperty();
         if (p == null) {
             return null;
         }
         if (!p.isArray()) {
             long length = p.size();
-            return PropertyStates.longProperty("LENGTH", length);
+            return PropertyValues.newLong(length);
         }
         // TODO what is the expected result for LENGTH(multiValueProperty)?
         throw new IllegalArgumentException("LENGTH(x) on multi-valued property is not supported");
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         if (v != null) {
-            switch (v.getType()) {
+            switch (v.getType().tag()) {
             case PropertyType.LONG:
             case PropertyType.DECIMAL:
             case PropertyType.DOUBLE:
@@ -81,7 +81,7 @@
             default:
                 throw new IllegalArgumentException(
                         "Can not compare the length with a constant of type "
-                                + PropertyType.nameFromValue(v.getType()) +
+                                + PropertyType.nameFromValue(v.getType().tag()) +
                                 " and value " + v.toString());
             }
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/LowerCaseImpl.java	(working copy)
@@ -18,13 +18,13 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-
 /**
  * The function "lower(..)".
  */
@@ -51,7 +51,7 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         PropertyState p = operand.currentProperty();
         if (p == null) {
             return null;
@@ -59,13 +59,13 @@
         // TODO what is the expected result of LOWER(x) for an array property?
         // currently throws an exception
         String value = p.getValue(STRING);
-        return PropertyStates.stringProperty(p.getName(), value.toLowerCase());
+        return PropertyValues.newString(value.toLowerCase());
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         // LOWER(x) implies x is not null
-        operand.restrict(f, Operator.NOT_EQUAL, null);        
+        operand.restrict(f, Operator.NOT_EQUAL, null);
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/UpperCaseImpl.java	(working copy)
@@ -18,13 +18,13 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-
 /**
  * The function "upper(..)".
  */
@@ -51,7 +51,7 @@
     }
 
     @Override
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         PropertyState p = operand.currentProperty();
         if (p == null) {
             return null;
@@ -59,11 +59,11 @@
         // TODO what is the expected result of UPPER(x) for an array property?
         // currently throws an exception
         String value = p.getValue(STRING);
-        return PropertyStates.stringProperty(p.getName(), value.toUpperCase());
+        return PropertyValues.newString(value.toUpperCase());
     }
 
     @Override
-    public void restrict(FilterImpl f, Operator operator, CoreValue v) {
+    public void restrict(FilterImpl f, Operator operator, PropertyValue v) {
         // UPPER(x) implies x is not null
         operand.restrict(f, Operator.NOT_EQUAL, null);
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/PropertyExistenceImpl.java	(working copy)
@@ -18,8 +18,6 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
 
 /**
@@ -38,8 +36,7 @@
 
     @Override
     public boolean evaluate() {
-        PropertyState p = selector.currentProperty(propertyName);
-        return p != null;
+        return selector.currentProperty(propertyName) != null;
     }
 
     @Override
@@ -59,7 +56,7 @@
     @Override
     public void restrict(FilterImpl f) {
         if (f.getSelector() == selector) {
-            f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
+            f.restrictProperty(propertyName, Operator.NOT_EQUAL, null);
         }
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/ColumnImpl.java	(working copy)
@@ -18,9 +18,8 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
-import org.apache.jackrabbit.oak.query.Query;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 /**
  * A result column expression.
@@ -55,14 +54,14 @@
         }
     }
 
-    public PropertyState currentProperty() {
+    public PropertyValue currentProperty() {
         if (propertyName == null) {
             // TODO for SELECT * FROM queries, currently return the path (for testing only)
             String p = selector.currentPath();
             if (p == null) {
                 return null;
             }
-            return PropertyStates.stringProperty(Query.JCR_PATH, p);
+            return PropertyValues.newString(p);
         }
         return selector.currentProperty(propertyName);
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/FullTextSearchImpl.java	(working copy)
@@ -18,18 +18,19 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+
 import java.text.ParseException;
 import java.util.ArrayList;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.query.ast.ComparisonImpl.LikePattern;
 import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
-import static org.apache.jackrabbit.oak.api.Type.STRING;
-import static org.apache.jackrabbit.oak.api.Type.STRINGS;
-
 /**
  * A fulltext "contains(...)" condition.
  */
@@ -77,7 +78,7 @@
     public boolean evaluate() {
         StringBuilder buff = new StringBuilder();
         if (propertyName != null) {
-            PropertyState p = selector.currentProperty(propertyName);
+            PropertyValue p = selector.currentProperty(propertyName);
             if (p == null) {
                 return false;
             }
@@ -95,9 +96,9 @@
         // if a fulltext index is used, to avoid filtering too much
         // (we don't know what exact options are used in the fulltext index)
         // (stop word, special characters,...)
-        CoreValue v = fullTextSearchExpression.currentValue();
+        PropertyValue v = fullTextSearchExpression.currentValue();
         try {
-            FullTextExpression expr = FullTextParser.parse(v.getString());
+            FullTextExpression expr = FullTextParser.parse(v.getValue(Type.STRING));
             return expr.evaluate(buff.toString());
         } catch (ParseException e) {
             throw new IllegalArgumentException("Invalid expression: " + fullTextSearchExpression, e);
@@ -122,10 +123,10 @@
     public void restrict(FilterImpl f) {
         if (propertyName != null) {
             if (f.getSelector() == selector) {
-                f.restrictProperty(propertyName, Operator.NOT_EQUAL, (CoreValue) null);
+                f.restrictProperty(propertyName, Operator.NOT_EQUAL, null);
             }
         }
-        f.restrictFulltextCondition(fullTextSearchExpression.currentValue().getString());
+        f.restrictFulltextCondition(fullTextSearchExpression.currentValue().getValue(Type.STRING));
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/StaticOperandImpl.java	(working copy)
@@ -18,13 +18,13 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * The base class for static operands (literal, bind variables).
  */
 public abstract class StaticOperandImpl extends AstElement {
 
-    abstract CoreValue currentValue();
+    abstract PropertyValue currentValue();
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/AstElementFactory.java	(working copy)
@@ -13,7 +13,7 @@
  */
 package org.apache.jackrabbit.oak.query.ast;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * A factory for syntax tree elements.
@@ -84,7 +84,7 @@
         return new LengthImpl(propertyValue);
     }
 
-    public LiteralImpl literal(CoreValue literalValue) {
+    public LiteralImpl literal(PropertyValue literalValue) {
         return new LiteralImpl(literalValue);
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/IndexRowImpl.java	(working copy)
@@ -18,8 +18,8 @@
  */
 package org.apache.jackrabbit.oak.query.index;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * A simple index row implementation.
@@ -38,7 +38,7 @@
     }
 
     @Override
-    public CoreValue getValue(String columnName) {
+    public PropertyValue getValue(String columnName) {
         return null;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/index/FilterImpl.java	(working copy)
@@ -18,19 +18,20 @@
  */
 package org.apache.jackrabbit.oak.query.index;
 
-import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.query.ast.Operator;
-import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
-import org.apache.jackrabbit.oak.spi.query.Filter;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
+
 import javax.jcr.PropertyType;
 
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.query.ast.Operator;
+import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+
 /**
  * A filter or lookup condition.
  */
@@ -182,46 +183,47 @@
         x.propertyType = propertyType;
     }
 
-    public void restrictProperty(String propertyName, Operator op, CoreValue value) {
+    public void restrictProperty(String propertyName, Operator op, PropertyValue v) {
         PropertyRestriction x = propertyRestrictions.get(propertyName);
         if (x == null) {
             x = new PropertyRestriction();
             x.propertyName = propertyName;
             propertyRestrictions.put(propertyName, x);
         }
-        CoreValue oldFirst = x.first, oldLast = x.last;
+        PropertyValue oldFirst = x.first;
+        PropertyValue oldLast = x.last;
         switch (op) {
         case EQUAL:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
         case NOT_EQUAL:
-            if (value != null) {
+            if (v != null) {
                 throw new IllegalArgumentException("NOT_EQUAL only supported for NOT_EQUAL NULL");
             }
             break;
         case GREATER_THAN:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = false;
             break;
         case GREATER_OR_EQUAL:
-            x.first = maxValue(oldFirst, value);
+            x.first = maxValue(oldFirst, v);
             x.firstIncluding = x.first == oldFirst ? x.firstIncluding : true;
             break;
         case LESS_THAN:
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = false;
             break;
         case LESS_OR_EQUAL:
-            x.last = minValue(oldLast, value);
+            x.last = minValue(oldLast, v);
             x.lastIncluding = x.last == oldLast ? x.lastIncluding : true;
             break;
         case LIKE:
             // LIKE is handled in the fulltext index
             x.isLike = true;
-            x.first = value;
+            x.first = v;
             break;
         }
         if (x.first != null && x.last != null) {
@@ -233,18 +235,18 @@
         }
     }
 
-    static CoreValue maxValue(CoreValue a, CoreValue b) {
+    static PropertyValue maxValue(PropertyValue a, PropertyValue b) {
         if (a == null) {
             return b;
         }
         return a.compareTo(b) < 0 ? b : a;
     }
 
-    static CoreValue minValue(CoreValue a, CoreValue b) {
+    static PropertyValue minValue(PropertyValue a, PropertyValue b) {
         if (a == null) {
             return b;
         }
-        return a.compareTo(b) < 0 ? a : b;
+        return a.compareTo(b) <= 0 ? a : b;
     }
 
     @Override
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/ResultRowImpl.java	(working copy)
@@ -16,10 +16,10 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.ResultRow;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * A query result row that keeps all data in memory.
@@ -28,10 +28,10 @@
 
     private final Query query;
     private final String[] paths;
-    private final CoreValue[] values;
-    private final CoreValue[][] orderValues;
+    private final PropertyValue[] values;
+    private final PropertyValue[] orderValues;
 
-    ResultRowImpl(Query query, String[] paths, CoreValue[] values, CoreValue[][] orderValues) {
+    ResultRowImpl(Query query, String[] paths, PropertyValue[] values, PropertyValue[] orderValues) {
         this.query = query;
         this.paths = paths;
         this.values = values;
@@ -58,13 +58,13 @@
     }
 
     @Override
-    public CoreValue getValue(String columnName) {
+    public PropertyValue getValue(String columnName) {
         return values[query.getColumnIndex(columnName)];
     }
 
     @Override
-    public CoreValue[] getValues() {
-        CoreValue[] v2 = new CoreValue[values.length];
+    public PropertyValue[] getValues() {
+        PropertyValue[] v2 = new PropertyValue[values.length];
         System.arraycopy(values, 0, v2, 0, values.length);
         return v2;
     }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryEngineImpl.java	(working copy)
@@ -22,12 +22,11 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.CoreValueFactory;
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.query.index.TraversingIndex;
 import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -43,12 +42,10 @@
     static final String JQOM = "JCR-JQOM";
 
     private final NodeStore store;
-    private final CoreValueFactory vf;
     private final QueryIndexProvider indexProvider;
 
     public QueryEngineImpl(NodeStore store, QueryIndexProvider indexProvider) {
         this.store = store;
-        this.vf = store.getValueFactory();
         this.indexProvider = indexProvider;
     }
 
@@ -72,16 +69,16 @@
     private Query parseQuery(String statement, String language) throws ParseException {
         Query q;
         if (SQL2.equals(language) || JQOM.equals(language)) {
-            SQL2Parser parser = new SQL2Parser(vf);
+            SQL2Parser parser = new SQL2Parser();
             q = parser.parse(statement);
         } else if (SQL.equals(language)) {
-            SQL2Parser parser = new SQL2Parser(vf);
+            SQL2Parser parser = new SQL2Parser();
             parser.setSupportSQL1(true);
             q = parser.parse(statement);
         } else if (XPATH.equals(language)) {
             XPathToSQL2Converter converter = new XPathToSQL2Converter();
             String sql2 = converter.convert(statement);
-            SQL2Parser parser = new SQL2Parser(vf);
+            SQL2Parser parser = new SQL2Parser();
             try {
                 q = parser.parse(sql2);
             } catch (ParseException e) {
@@ -94,7 +91,7 @@
     }
 
     public ResultImpl executeQuery(String statement, String language, 
-            long limit, long offset, Map<String, ? extends CoreValue> bindings,
+            long limit, long offset, Map<String, ? extends PropertyValue> bindings,
             Root root,
             NamePathMapper namePathMapper) throws ParseException {
         Query q = parseQuery(statement, language);
@@ -103,7 +100,7 @@
         q.setLimit(limit);
         q.setOffset(offset);
         if (bindings != null) {
-            for (Entry<String, ? extends CoreValue> e : bindings.entrySet()) {
+            for (Entry<String, ? extends PropertyValue> e : bindings.entrySet()) {
                 q.bindValue(e.getKey(), e.getValue());
             }
         }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/query/SQL2Parser.java	(working copy)
@@ -16,11 +16,9 @@
  */
 package org.apache.jackrabbit.oak.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-import org.apache.jackrabbit.oak.api.CoreValueFactory;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.query.ast.AstElementFactory;
-import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.BindVariableValueImpl;
 import org.apache.jackrabbit.oak.query.ast.ColumnImpl;
 import org.apache.jackrabbit.oak.query.ast.ConstraintImpl;
@@ -28,12 +26,15 @@
 import org.apache.jackrabbit.oak.query.ast.JoinConditionImpl;
 import org.apache.jackrabbit.oak.query.ast.JoinType;
 import org.apache.jackrabbit.oak.query.ast.LiteralImpl;
+import org.apache.jackrabbit.oak.query.ast.Operator;
 import org.apache.jackrabbit.oak.query.ast.OrderingImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyExistenceImpl;
 import org.apache.jackrabbit.oak.query.ast.PropertyValueImpl;
 import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
 import org.apache.jackrabbit.oak.query.ast.SourceImpl;
 import org.apache.jackrabbit.oak.query.ast.StaticOperandImpl;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 
 import javax.jcr.PropertyType;
 import java.math.BigDecimal;
@@ -66,7 +67,7 @@
     private int currentTokenType;
     private String currentToken;
     private boolean currentTokenQuoted;
-    private CoreValue currentValue;
+    private PropertyValue currentValue;
     private ArrayList<String> expected;
 
     // The bind variables
@@ -80,17 +81,14 @@
     private boolean allowNumberLiterals = true;
 
     private final AstElementFactory factory = new AstElementFactory();
-    private final CoreValueFactory valueFactory;
 
     private boolean supportSQL1;
 
     /**
      * Create a new parser. A parser can be re-used, but it is not thread safe.
      *
-     * @param valueFactory the value factory
      */
-    public SQL2Parser(CoreValueFactory valueFactory) {
-        this.valueFactory = valueFactory;
+    public SQL2Parser() {
     }
 
     /**
@@ -136,7 +134,7 @@
         if (!currentToken.isEmpty()) {
             throw getSyntaxError("<end>");
         }
-        Query q = new Query(source, constraint, orderings, columnArray, valueFactory);
+        Query q = new Query(source, constraint, orderings, columnArray);
         q.setExplain(explain);
         q.setMeasure(measure);
         try {
@@ -186,9 +184,9 @@
     private String readName() throws ParseException {
         if (readIf("[")) {
             if (currentTokenType == VALUE) {
-                CoreValue value = readString();
+                PropertyValue value = readString();
                 read("]");
-                return value.getString();
+                return value.getValue(Type.STRING);
             } else {
                 int level = 1;
                 StringBuilder buff = new StringBuilder();
@@ -356,8 +354,8 @@
                     if (!(esc instanceof LiteralImpl)) {
                         throw getSyntaxError("only ESCAPE '\' is supported");
                     }
-                    CoreValue v = ((LiteralImpl) esc).getLiteralValue();
-                    if (!v.getString().equals("\\")) {
+                    PropertyValue v = ((LiteralImpl) esc).getLiteralValue();
+                    if (!v.getValue(Type.STRING).equals("\\")) {
                         throw getSyntaxError("only ESCAPE '\' is supported");
                     }
                 }
@@ -518,7 +516,7 @@
         } else if ("PROPERTY".equalsIgnoreCase(functionName)) {
             PropertyValueImpl pv = parsePropertyValue(readName());
             read(",");
-            op = factory.propertyValue(pv.getSelectorName(), pv.getPropertyName(), readString().getString());
+            op = factory.propertyValue(pv.getSelectorName(), pv.getPropertyName(), readString().getValue(Type.STRING));
         } else {
             throw getSyntaxError("LENGTH, NAME, LOCALNAME, SCORE, LOWER, UPPER, or PROPERTY");
         }
@@ -542,19 +540,19 @@
             if (currentTokenType != VALUE) {
                 throw getSyntaxError("number");
             }
-            int valueType = currentValue.getType();
+            int valueType = currentValue.getType().tag();
             switch (valueType) {
             case PropertyType.LONG:
-                currentValue = valueFactory.createValue(-currentValue.getLong());
+                currentValue = PropertyValues.newLong(-currentValue.getValue(Type.LONG));
                 break;
             case PropertyType.DOUBLE:
-                currentValue = valueFactory.createValue(-currentValue.getDouble());
+                currentValue = PropertyValues.newDouble(-currentValue.getValue(Type.DOUBLE));
                 break;
             case PropertyType.BOOLEAN:
-                currentValue = valueFactory.createValue(!currentValue.getBoolean());
+                currentValue = PropertyValues.newBoolean(!currentValue.getValue(Type.BOOLEAN));
                 break;
             case PropertyType.DECIMAL:
-                currentValue = valueFactory.createValue(currentValue.getDecimal().negate());
+                currentValue = PropertyValues.newDecimal(currentValue.getValue(Type.DECIMAL).negate());
                 break;
             default:
                 throw getSyntaxError("Illegal operation: -" + currentValue);
@@ -577,10 +575,10 @@
             }
             return var;
         } else if (readIf("TRUE")) {
-            LiteralImpl literal = getUncastLiteral(valueFactory.createValue(true));
+            LiteralImpl literal = getUncastLiteral(PropertyValues.newBoolean(true));
             return literal;
         } else if (readIf("FALSE")) {
-            LiteralImpl literal = getUncastLiteral(valueFactory.createValue(false));
+            LiteralImpl literal = getUncastLiteral(PropertyValues.newBoolean(false));
             return literal;
         } else if (readIf("CAST")) {
             read("(");
@@ -589,7 +587,7 @@
                 throw getSyntaxError("literal");
             }
             LiteralImpl literal = (LiteralImpl) op;
-            CoreValue value = literal.getLiteralValue();
+            PropertyValue value = literal.getLiteralValue();
             read("AS");
             value = parseCastAs(value);
             read(")");
@@ -604,8 +602,8 @@
                         throw getSyntaxError("literal");
                     }
                     LiteralImpl literal = (LiteralImpl) op;
-                    CoreValue value = literal.getLiteralValue();
-                    value = valueFactory.createValue(value.getString(), PropertyType.DATE);
+                    PropertyValue value = literal.getLiteralValue();
+                    value = PropertyValues.newDate(value.getValue(Type.STRING));
                     literal = factory.literal(value);
                     return literal;
                 }
@@ -620,44 +618,23 @@
      * @param value the original value
      * @return the literal
      */
-    private LiteralImpl getUncastLiteral(CoreValue value) {
+    private LiteralImpl getUncastLiteral(PropertyValue value) {
         return factory.literal(value);
     }
 
-    private CoreValue parseCastAs(CoreValue value) throws ParseException {
+    private PropertyValue parseCastAs(PropertyValue value)
+            throws ParseException {
         if (currentTokenQuoted) {
             throw getSyntaxError("data type (STRING|BINARY|...)");
         }
         int propertyType = getPropertyTypeFromName(currentToken);
         read();
-        switch (propertyType) {
-        case PropertyType.STRING:
-            return valueFactory.createValue(value.getString());
-        case PropertyType.BINARY:
-            return valueFactory.createValue(value.getString(), PropertyType.BINARY);
-        case PropertyType.DATE:
-            return valueFactory.createValue(value.getString(), PropertyType.DATE);
-        case PropertyType.LONG:
-            return valueFactory.createValue(value.getLong());
-        case PropertyType.DOUBLE:
-            return valueFactory.createValue(value.getDouble());
-        case PropertyType.DECIMAL:
-            return valueFactory.createValue(value.getDecimal());
-        case PropertyType.BOOLEAN:
-            return valueFactory.createValue(value.getBoolean());
-        case PropertyType.NAME:
-            return valueFactory.createValue(value.getString(), PropertyType.NAME);
-        case PropertyType.PATH:
-            return valueFactory.createValue(value.getString(), PropertyType.PATH);
-        case PropertyType.REFERENCE:
-            return valueFactory.createValue(value.getString(), PropertyType.REFERENCE);
-        case PropertyType.WEAKREFERENCE:
-            return valueFactory.createValue(value.getString(), PropertyType.WEAKREFERENCE);
-        case PropertyType.URI:
-            return valueFactory.createValue(value.getString(), PropertyType.URI);
-        default:
+
+        PropertyValue v = PropertyValues.convert(value, propertyType, null);
+        if (v == null) {
             throw getSyntaxError("data type (STRING|BINARY|...)");
         }
+        return v;
     }
 
     /**
@@ -807,7 +784,7 @@
         }
         String s;
         if (currentTokenType == VALUE) {
-            s = currentValue.getString();
+            s = currentValue.getValue(Type.STRING);
         } else {
             s = currentToken;
         }
@@ -815,11 +792,11 @@
         return s;
     }
 
-    private CoreValue readString() throws ParseException {
+    private PropertyValue readString() throws ParseException {
         if (currentTokenType != VALUE) {
             throw getSyntaxError("string value");
         }
-        CoreValue value = currentValue;
+        PropertyValue value = currentValue;
         read();
         return value;
     }
@@ -999,7 +976,7 @@
                         break;
                     }
                     checkLiterals(false);
-                    currentValue = valueFactory.createValue(number);
+                    currentValue = PropertyValues.newLong(number);
                     currentTokenType = VALUE;
                     currentToken = "0";
                     parseIndex = i;
@@ -1035,7 +1012,7 @@
                 // (not in the JCR 1.0 spec)
                 // (confusing isn't it?)
                 currentTokenType = IDENTIFIER;
-                currentToken = currentValue.getString();
+                currentToken = currentValue.getValue(Type.STRING);
             }
             return;
         case CHAR_END:
@@ -1069,7 +1046,7 @@
         }
         currentToken = "'";
         checkLiterals(false);
-        currentValue = valueFactory.createValue(result);
+        currentValue = PropertyValues.newString(result);
         parseIndex = i;
         currentTokenType = VALUE;
     }
@@ -1112,7 +1089,7 @@
         }
         checkLiterals(false);
 
-        currentValue = valueFactory.createValue(bd);
+        currentValue = PropertyValues.newDecimal(bd);
         currentTokenType = VALUE;
     }
 
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProviderImpl.java	(working copy)
@@ -36,6 +36,8 @@
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.plugins.memory.CoreValues;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryPropertyBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
 import org.apache.jackrabbit.oak.spi.security.user.MembershipProvider;
 import org.apache.jackrabbit.oak.spi.security.user.UserConfig;
@@ -151,7 +153,7 @@
                 memberPaths = Iterables.transform(vs, new Function<CoreValue,String>() {
                     @Override
                     public String apply(@Nullable CoreValue value) {
-                        return identifierManager.getPath(value);
+                        return identifierManager.getPath(PropertyStates.createProperty("", value.getString(), value.getType()));
                     }
                 });
             }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserProviderImpl.java	(working copy)
@@ -30,7 +30,6 @@
 
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.api.security.user.Impersonation;
-import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Result;
 import org.apache.jackrabbit.oak.api.ResultRow;
@@ -38,6 +37,7 @@
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
 import org.apache.jackrabbit.oak.spi.security.principal.TreeBasedPrincipal;
 import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
@@ -206,14 +206,13 @@
         // can be omitted as principals names are stored in user defined
         // index as well.
         try {
-            CoreValue bindValue = valueFactory.createValue(principal.getName());
             StringBuilder stmt = new StringBuilder();
             stmt.append("SELECT * FROM [").append(UserConstants.NT_REP_AUTHORIZABLE).append(']');
             stmt.append("WHERE [").append(UserConstants.REP_PRINCIPAL_NAME).append("] = $principalName");
 
             Result result = root.getQueryEngine().executeQuery(stmt.toString(),
                     Query.JCR_SQL2, 1, 0,
-                    Collections.singletonMap("principalName", bindValue),
+                    Collections.singletonMap("principalName", PropertyValues.newString(principal.getName())),
                     root, new NamePathMapper.Default());
 
             Iterator<? extends ResultRow> rows = result.getRows().iterator();
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/SessionQueryEngine.java	(working copy)
@@ -21,6 +21,7 @@
 import java.util.Map;
 
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
 
 /**
  * The query engine allows to parse and execute queries.
@@ -61,7 +62,7 @@
      * @throws IllegalArgumentException if there was an error executing the query
      */
     Result executeQuery(String statement, String language,
-            long limit, long offset, Map<String, ? extends CoreValue> bindings,
+            long limit, long offset, Map<String, ? extends PropertyValue> bindings,
             Root root, NamePathMapper namePathMapper) throws ParseException;
 
     // TODO pass namespace mapping
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/api/ResultRow.java	(working copy)
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.api;
 
+import org.apache.jackrabbit.oak.spi.query.PropertyValue;
+
 /**
  * A query result row.
  */
@@ -25,8 +27,8 @@
 
     String getPath(String selectorName);
 
-    CoreValue getValue(String columnName);
+    PropertyValue getValue(String columnName);
 
-    CoreValue[] getValues();
+    PropertyValue[] getValues();
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/IndexRow.java	(working copy)
@@ -18,7 +18,6 @@
  */
 package org.apache.jackrabbit.oak.spi.query;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
 
 /**
  * A row returned by the index.
@@ -40,6 +39,6 @@
      * @param columnName the column name
      * @return the value, or null if not available
      */
-    CoreValue getValue(String columnName);
+    PropertyValue getValue(String columnName);
 
 }
Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValue.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValue.java	(revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValue.java	(revision 0)
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.spi.query;
+
+import java.util.Calendar;
+import java.util.Iterator;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.util.ISO8601;
+
+public class PropertyValue implements PropertyState, Comparable<PropertyValue> {
+
+    private final PropertyState ps;
+
+    protected PropertyValue(PropertyState ps) {
+        this.ps = ps;
+    }
+
+    public String getName() {
+        return ps.getName();
+    }
+
+    public boolean isArray() {
+        return ps.isArray();
+    }
+
+    public Type<?> getType() {
+        return ps.getType();
+    }
+
+    public <T> T getValue(Type<T> type) {
+        return ps.getValue(type);
+    }
+
+    public <T> T getValue(Type<T> type, int index) {
+        return ps.getValue(type, index);
+    }
+
+    public long size() {
+        return ps.size();
+    }
+
+    public long size(int index) {
+        return ps.size(index);
+    }
+
+    public int count() {
+        return ps.count();
+    }
+
+    public PropertyState unwrap() {
+        return ps;
+    }
+
+    public int compareTo(PropertyValue p2) {
+        if (getType().tag() != p2.getType().tag()) {
+            return Integer.signum(getType().tag() - p2.getType().tag());
+        }
+        switch (getType().tag()) {
+        case PropertyType.BINARY:
+            return compare(getValue(Type.BINARIES), p2.getValue(Type.BINARIES));
+        case PropertyType.DOUBLE:
+            return compare(getValue(Type.DOUBLES), p2.getValue(Type.DOUBLES));
+        case PropertyType.DATE:
+            return compareAsDate(getValue(Type.STRINGS),
+                    p2.getValue(Type.STRINGS));
+        default:
+            return compare(getValue(Type.STRINGS), p2.getValue(Type.STRINGS));
+        }
+    }
+
+    private static <T extends Comparable<T>> int compare(Iterable<T> p1,
+            Iterable<T> p2) {
+        Iterator<T> i1 = p1.iterator();
+        Iterator<T> i2 = p2.iterator();
+        while (i1.hasNext() || i2.hasNext()) {
+            if (!i1.hasNext()) {
+                return 1;
+            }
+            if (!i2.hasNext()) {
+                return -1;
+            }
+            int compare = i1.next().compareTo(i2.next());
+            if (compare != 0) {
+                return compare;
+            }
+        }
+        return 0;
+    }
+
+    private static int compareAsDate(Iterable<String> p1, Iterable<String> p2) {
+        Iterator<String> i1 = p1.iterator();
+        Iterator<String> i2 = p2.iterator();
+        while (i1.hasNext() || i2.hasNext()) {
+            if (!i1.hasNext()) {
+                return 1;
+            }
+            if (!i2.hasNext()) {
+                return -1;
+            }
+            String v1 = i1.next();
+            String v2 = i2.next();
+
+            Calendar c1 = ISO8601.parse(v1);
+            Calendar c2 = ISO8601.parse(v2);
+            int compare = -1;
+            if (c1 != null && c2 != null) {
+                compare = c1.compareTo(c2);
+            } else {
+                compare = v1.compareTo(v2);
+            }
+            if (compare != 0) {
+                return compare;
+            }
+        }
+        return 0;
+    }
+
+    // --------------------------------------------------------------< Object >
+
+    private String getInternalString() {
+        StringBuilder sb = new StringBuilder();
+        Iterator<String> iterator = getValue(Type.STRINGS).iterator();
+        while (iterator.hasNext()) {
+            sb.append(iterator.next());
+            if (iterator.hasNext()) {
+                sb.append(",");
+            }
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return getType().tag() ^ getInternalString().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        } else if (o instanceof PropertyValue) {
+            return compareTo((PropertyValue) o) == 0;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getInternalString();
+    }
+
+}

Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValue.java
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision Rev URL
Added: svn:eol-style
   + native

Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java	(revision 0)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java	(revision 0)
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.spi.query;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Iterator;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+
+public class PropertyValues {
+
+    private PropertyValues() {
+    }
+
+    // TODO consistent naming
+
+    @CheckForNull
+    public static PropertyValue create(PropertyState property) {
+        if (property == null) {
+            return null;
+        }
+        return new PropertyValue(property);
+    }
+
+    @Nonnull
+    public static PropertyValue newString(String value) {
+        return new PropertyValue(PropertyStates.stringProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newString(Iterable<String> value) {
+        return new PropertyValue(PropertyStates.stringProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newLong(Long value) {
+        return new PropertyValue(PropertyStates.longProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newDouble(Double value) {
+        return new PropertyValue(PropertyStates.doubleProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newDecimal(BigDecimal value) {
+        return new PropertyValue(PropertyStates.decimalProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newBoolean(boolean value) {
+        return new PropertyValue(PropertyStates.booleanProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newDate(String value) {
+        return new PropertyValue(PropertyStates.dateProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newName(String value) {
+        return new PropertyValue(PropertyStates.nameProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newPath(String value) {
+        return new PropertyValue(PropertyStates.pathProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newReference(String value) {
+        return new PropertyValue(PropertyStates.referenceProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newWeakReference(String value) {
+        return new PropertyValue(
+                PropertyStates.weakreferenceProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newUri(String value) {
+        return new PropertyValue(PropertyStates.uriProperty("", value));
+    }
+
+    @Nonnull
+    public static PropertyValue newBinary(byte[] value) {
+        return new PropertyValue(PropertyStates.binaryProperty("", value));
+    }
+
+    // --
+
+    public static boolean match(PropertyValue p1, PropertyState p2) {
+        return match(p1, create(p2));
+    }
+
+    public static boolean match(PropertyState p1, PropertyValue p2) {
+        return match(create(p1), p2);
+    }
+
+    public static boolean match(PropertyValue p1, PropertyValue p2) {
+        if (p1.getType().tag() != p2.getType().tag()) {
+            return false;
+        }
+
+        switch (p1.getType().tag()) {
+        case PropertyType.BINARY:
+            if (p1.isArray() && !p2.isArray()) {
+                return contains(p1.getValue(Type.BINARIES),
+                        p2.getValue(Type.BINARY));
+            }
+            if (!p1.isArray() && p2.isArray()) {
+                return contains(p2.getValue(Type.BINARIES),
+                        p2.getValue(Type.BINARY));
+            }
+        default:
+            if (p1.isArray() && !p2.isArray()) {
+                return contains(p1.getValue(Type.STRINGS),
+                        p2.getValue(Type.STRING));
+            }
+            if (!p1.isArray() && p2.isArray()) {
+                return contains(p2.getValue(Type.STRINGS),
+                        p2.getValue(Type.STRING));
+            }
+        }
+        // both arrays or both single values
+        return p1.compareTo(p2) == 0;
+
+    }
+
+    private static <T extends Comparable<T>> boolean contains(Iterable<T> p1,
+            T p2) {
+        Iterator<T> i1 = p1.iterator();
+        while (i1.hasNext()) {
+            int compare = i1.next().compareTo(p2);
+            if (compare == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // --
+    /**
+     * Convert a value to the given target type, if possible.
+     * 
+     * @param v
+     *            the value to convert
+     * @param targetType
+     *            the target property type
+     * @return the converted value, or null if converting is not possible
+     */
+    public static PropertyValue convert(PropertyState value, int targetType,
+            NamePathMapper mapper) {
+        // TODO support full set of conversion features defined in the JCR spec
+        // at 3.6.4 Property Type Conversion
+        // re-use existing code if possible
+        try {
+            switch (targetType) {
+            case PropertyType.STRING:
+                return newString(value.getValue(Type.STRING));
+            case PropertyType.DATE:
+                return newDate(value.getValue(Type.STRING));
+            case PropertyType.LONG:
+                return newLong(value.getValue(Type.LONG));
+            case PropertyType.DOUBLE:
+                return newDouble(value.getValue(Type.DOUBLE));
+            case PropertyType.DECIMAL:
+                return newDecimal(value.getValue(Type.DECIMAL));
+            case PropertyType.BOOLEAN:
+                return newBoolean(value.getValue(Type.BOOLEAN));
+            case PropertyType.NAME:
+                return newName(getOakPath(value.getValue(Type.STRING), mapper));
+            case PropertyType.PATH:
+                return newPath(value.getValue(Type.STRING));
+            case PropertyType.REFERENCE:
+                return newReference(value.getValue(Type.STRING));
+            case PropertyType.WEAKREFERENCE:
+                return newWeakReference(value.getValue(Type.STRING));
+            case PropertyType.URI:
+                return newUri(value.getValue(Type.STRING));
+            case PropertyType.BINARY:
+                try {
+                    byte[] data = value.getValue(Type.STRING).getBytes("UTF-8");
+                    return newBinary(data);
+                } catch (IOException e) {
+                    // I don't know in what case that could really occur
+                    // except if UTF-8 isn't supported
+                    throw new IllegalArgumentException(
+                            value.getValue(Type.STRING), e);
+                }
+            }
+            return null;
+            // throw new IllegalArgumentException("Unknown property type: " +
+            // targetType);
+        } catch (UnsupportedOperationException e) {
+            // TODO detect unsupported conversions, so that no exception is
+            // thrown
+            // because exceptions are slow
+            return null;
+            // throw new IllegalArgumentException("<unsupported conversion of "
+            // +
+            // v + " (" + PropertyType.nameFromValue(v.getType()) + ") to type "
+            // +
+            // PropertyType.nameFromValue(targetType) + ">");
+        }
+    }
+
+    public static String getOakPath(String jcrPath, NamePathMapper mapper) {
+        if (mapper == null) {
+            // to simplify testing, a getNamePathMapper isn't required
+            return jcrPath;
+        }
+        String p = mapper.getOakPath(jcrPath);
+        if (p == null) {
+            throw new IllegalArgumentException("Not a valid JCR path: "
+                    + jcrPath);
+        }
+        return p;
+    }
+
+}

Property changes on: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/PropertyValues.java
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision Rev URL
Added: svn:eol-style
   + native

Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java
===================================================================
--- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java	(revision 1397066)
+++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/query/Filter.java	(working copy)
@@ -21,8 +21,6 @@
 import java.util.Collection;
 import javax.jcr.PropertyType;
 
-import org.apache.jackrabbit.oak.api.CoreValue;
-
 /**
  * The filter for an index lookup.
  */
@@ -79,7 +77,7 @@
         /**
          * The first value to read, or null to read from the beginning.
          */
-        public CoreValue first;
+        public PropertyValue first;
 
         /**
          * Whether values that match the first should be returned.
@@ -89,7 +87,7 @@
         /**
          * The last value to read, or null to read until the end.
          */
-        public CoreValue last;
+        public PropertyValue last;
 
         /**
          * Whether values that match the last should be returned.
