Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/PathConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/PathConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/PathConstraintTest.java	(revision 0)
@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+
+/**
+ * <code>PathConstraintTest</code>...
+ */
+public class PathConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(PathConstraintTest.class);
+
+    protected int getType() {
+        return PropertyType.PATH;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() throws NamespaceException, IllegalNameException, MalformedPathException {
+        return new String[] {"12345", "*"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        return new String[] {"/abc/*", "/", "abc/*", "/*", "/abc/def"};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return new String[] {
+                resolver.getQPath("/abc").getString() + "/*",
+                resolver.getQPath("/").getString(),
+                resolver.getQPath("abc").getString() + "/*",
+                resolver.getQPath("/").getString() + "*",
+                resolver.getQPath("/abc/def").getString()};
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        QValue root = valueFactory.create(resolver.getQPath("/"));
+        QValue abs = valueFactory.create(resolver.getQPath("/uvw/xyz"));
+        QValue rel = valueFactory.create(resolver.getQPath("uvw/xyz"));
+        return new QValue[] {abs,abs,rel,root,abs};
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(23);
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\PathConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraintTest.java	(revision 0)
@@ -0,0 +1,193 @@
+/*
+ * 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.spi.commons.nodetype;
+
+import junit.framework.TestCase;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
+
+/**
+ * <code>ValueConstraintTest</code>...
+ */
+public abstract class ValueConstraintTest extends TestCase {
+
+    private static Logger log = LoggerFactory.getLogger(ValueConstraintTest.class);
+
+    protected QValueFactory valueFactory;
+    protected NamePathResolver resolver;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        valueFactory = QValueFactoryImpl.getInstance();
+        resolver = new DefaultNamePathResolver(new NamespaceResolver () {
+            public String getURI(String prefix) throws NamespaceException {
+                return prefix;
+            }
+            public String getPrefix(String uri) throws NamespaceException {
+                return uri;
+            }
+        });
+    }
+
+    protected abstract int getType();
+
+    protected abstract String[] getInvalidQualifiedDefinitions() throws NamespaceException, IllegalNameException, MalformedPathException;
+
+    protected abstract String[] getDefinitions() throws RepositoryException;
+
+    protected abstract String[] getQualifiedDefinitions() throws RepositoryException;
+
+    protected abstract QValue[] createNonMatchingValues() throws RepositoryException;
+
+    protected abstract QValue createOtherValueType() throws RepositoryException;
+
+
+    private ValueConstraint createValueConstraint(String qDefinition) throws RepositoryException {
+        return ValueConstraint.create(getType(), qDefinition);
+    }
+
+    private ValueConstraint createValueConstraint(String definition,
+                                                  NamePathResolver resolver) throws RepositoryException {
+        return ValueConstraint.create(getType(), definition, resolver);
+    }
+
+    public void testCreateFromNull() {
+        try {
+            createValueConstraint(null);
+            fail("attempt to create a value constraint from null should fail.");
+        } catch (Exception e) {
+            // ok
+        }
+    }
+
+    public void testCreateValueConstraints() throws RepositoryException {
+        String[] defs = getDefinitions();
+        for (int i = 0; i < defs.length; i++) {
+            ValueConstraint vc = createValueConstraint(defs[i], resolver);
+            assertEquals(defs[i], vc.getDefinition(resolver));
+        }
+    }
+
+    public void testCreateValueConstraints2() throws RepositoryException {
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            assertEquals(qDefs[i], vc.getQualifiedDefinition());
+        }
+    }
+
+    public void testCreateInvalidValueConstraints() throws RepositoryException {
+        try {
+            String[] invalidQDefs = getInvalidQualifiedDefinitions();
+            for (int i = 0; i < invalidQDefs.length; i++) {
+                createValueConstraint(invalidQDefs[i]);
+                fail("Creating an invalid definition should throw InvalidConstraintException");
+            }
+        } catch (InvalidConstraintException e) {
+            //ok
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public void testGetDefinition() throws RepositoryException {
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            assertNotNull(vc.getDefinition(resolver));
+        }
+    }
+
+    public void testGetQualifiedDefinition() throws RepositoryException {
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            assertNotNull(vc.getQualifiedDefinition());
+        }
+    }
+
+    public void testCheckNullValue() throws RepositoryException {
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            try {
+                vc.check(null);
+                fail("ValueConstraint.check(null) should throw ConstraintViolationException.");
+            } catch (ConstraintViolationException e) {
+                //ok
+            }
+        }
+    }
+
+    public void testCheckNonMatchingValue() throws RepositoryException {
+        QValue[] nonMatching = createNonMatchingValues();
+
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            if (i >= nonMatching.length) {
+                break;
+            }
+
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            try {
+                vc.check(nonMatching[i]);
+                fail("ValueConstraint.check() with non-matching value should throw ConstraintViolationException.");
+            } catch (ConstraintViolationException e) {
+                //ok
+            }
+        }
+    }
+
+    public void testCheckWrongValueType() throws RepositoryException {
+        QValue val = createOtherValueType();
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            try {
+                vc.check(val);
+                fail("ValueConstraint.check() with non-matching value should throw ConstraintViolationException.");
+            } catch (RepositoryException e) {
+                //ok
+            }
+        }
+    }
+
+    public void testEquals() throws RepositoryException {
+        String[] qDefs = getQualifiedDefinitions();
+        for (int i = 0; i < qDefs.length; i++) {
+            ValueConstraint vc = createValueConstraint(qDefs[i]);
+            ValueConstraint vc2 = createValueConstraint(qDefs[i]);
+            assertEquals(vc, vc2);
+
+            vc2 = createValueConstraint(vc.getQualifiedDefinition());
+            assertEquals(vc, vc2);
+        }
+    }
+}
\ No newline at end of file

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\ValueConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/DateConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/DateConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/DateConstraintTest.java	(revision 0)
@@ -0,0 +1,72 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.value.DateValue;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * <code>DateConstraintTest</code>...
+ */
+public class DateConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(DateConstraintTest.class);
+
+    protected int getType() {
+        return PropertyType.DATE;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() {
+        return new String[] {"abc", "-18", "1234567"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        Calendar c1 = Calendar.getInstance();
+        c1.setTimeInMillis(234567);
+
+        Calendar c2 = Calendar.getInstance();
+        c2.setTime(new Date());
+
+        StringBuffer b = new StringBuffer("(");
+        b.append(new DateValue(c1).getString());
+        b.append(",");
+        b.append(new DateValue(c2).getString());
+        b.append(")");
+
+        return new String[] {b.toString()};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return getDefinitions();
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        Calendar cd = Calendar.getInstance();
+        cd.setTimeInMillis(997);
+        return new QValue[] {valueFactory.create(cd)};
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(resolver.getQName("abc"));
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\DateConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ReferenceConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ReferenceConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ReferenceConstraintTest.java	(revision 0)
@@ -0,0 +1,68 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
+
+/**
+ * <code>DateConstraintTest</code>...
+ */
+public class ReferenceConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(ReferenceConstraintTest.class);
+
+    protected int getType() {
+        return PropertyType.REFERENCE;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() throws NamespaceException, IllegalNameException, MalformedPathException {
+        return new String[] {"12345", "", "abc"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        return new String[] {"12345", "abc", "jcr:abc"};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return new String[] {
+                resolver.getQName("12345").toString(),
+                resolver.getQName("abc").toString(),
+                resolver.getQName("jcr:abc").toString()
+        };
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        // TODO: reference constraints are not checked property -> not executable
+        throw new ConstraintViolationException();
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(23.56);
+    }
+
+    public void testCheckNonMatchingValue() throws RepositoryException {
+        // not executable
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\ReferenceConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/BooleanConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/BooleanConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/BooleanConstraintTest.java	(revision 0)
@@ -0,0 +1,79 @@
+/*
+ * 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.spi.commons.nodetype;
+
+import org.apache.jackrabbit.spi.QValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
+
+/**
+ * <code>BooleanConstraintTest</code>...
+ */
+public class BooleanConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(BooleanConstraintTest.class);
+
+    protected ValueConstraint createInvalidConstraint() throws RepositoryException {
+        return new BooleanConstraint("test");
+    }
+
+    protected ValueConstraint createValueConstraint(String definition) throws RepositoryException {
+        return new BooleanConstraint(definition);
+    }
+
+    protected int getType() {
+        return PropertyType.BOOLEAN;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() {
+        return new String[] {"test", "/abc/d", "12345"};
+    }
+
+    protected String[] getDefinitions() {
+        return new String[] { Boolean.TRUE.toString()};
+    }
+
+    protected String[] getQualifiedDefinitions() {
+        return getDefinitions();
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        return new QValue[] {
+                valueFactory.create(Boolean.FALSE.toString(), PropertyType.BOOLEAN),
+                valueFactory.create(Boolean.TRUE.toString(), PropertyType.BOOLEAN)
+        };
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(1345);
+    }
+
+    public void testTrueConstraint() throws RepositoryException, ConstraintViolationException {
+        ValueConstraint vc = new BooleanConstraint(Boolean.TRUE.toString());
+        vc.check(valueFactory.create(Boolean.TRUE.toString(), PropertyType.BOOLEAN));
+        try {
+           vc.check(valueFactory.create(Boolean.FALSE.toString(), PropertyType.BOOLEAN));
+            fail();
+        } catch (ConstraintViolationException e) {
+            // ok
+        }
+    }
+}
\ No newline at end of file

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\BooleanConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NameConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NameConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NameConstraintTest.java	(revision 0)
@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValue;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+
+/**
+ * <code>NameConstraintTest</code>...
+ */
+public class NameConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(NameConstraintTest.class);
+
+    protected int getType() {
+        return PropertyType.NAME;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() {
+        return new String[] {"12345", "", "abc"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        return new String[] {"12345", "abc", "jcr:abc"};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return new String[] {
+                resolver.getQName("12345").toString(),
+                resolver.getQName("abc").toString(),
+                resolver.getQName("jcr:abc").toString()
+        };
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        QValue v = valueFactory.create(resolver.getQName("xyz"));
+        return new QValue[] {v, v, v};
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(resolver.getQPath("xyz"));
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\NameConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/TestAll.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/TestAll.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/TestAll.java	(revision 0)
@@ -0,0 +1,40 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all test cases for compact node type tools.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("Compact Node Type Tools Tests");
+        suite.addTestSuite(CompactNodeTypeDefTest.class);
+        return suite;
+    }
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\compact\TestAll.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefTest.java	(revision 0)
@@ -0,0 +1,75 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping;
+import org.apache.jackrabbit.spi.commons.nodetype.NodeTypeDefDiff;
+import org.apache.jackrabbit.value.ValueFactoryImpl;
+
+public class CompactNodeTypeDefTest extends TestCase {
+
+    private static final String TEST_FILE = "cnd-reader-test-input.cnd";
+
+    public void testCompactNodeTypeDef() throws Exception {
+
+        // Read in node type def from test file
+        Reader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(TEST_FILE));
+        CompactNodeTypeDefReader cndReader = new CompactNodeTypeDefReader(reader, TEST_FILE,
+                new QNodeTypeDefinitionsBuilderImpl());
+
+        List ntdList1 = cndReader.getNodeTypeDefs();
+        NamespaceMapping nsm = cndReader.getNamespaceMapping();
+        NamePathResolver resolver = new DefaultNamePathResolver(nsm);
+
+        // Put imported node type def back into CND form with CND writer
+        StringWriter sw = new StringWriter();
+        CompactNodeTypeDefWriter.write(ntdList1, nsm, resolver, ValueFactoryImpl.getInstance(), sw);
+
+        // Rerun the reader on the product of the writer
+        cndReader = new CompactNodeTypeDefReader(new StringReader(sw.toString()), TEST_FILE,
+                new QNodeTypeDefinitionsBuilderImpl());
+
+        List ntdList2 = cndReader.getNodeTypeDefs();
+
+        if (ntdList1.size() == 0 || ntdList1.size() != ntdList2.size()) {
+            fail("Exported node type definition was not successfully read back in");
+        } else {
+            for(int k = 0; k < ntdList1.size(); k++) {
+                QNodeTypeDefinition ntd1 = (QNodeTypeDefinition) ntdList1.get(k);
+                QNodeTypeDefinition ntd2 = (QNodeTypeDefinition) ntdList2.get(k);
+
+                NodeTypeDefDiff diff = NodeTypeDefDiff.create(ntd1, ntd2);
+                if (diff.isModified() && !diff.isTrivial()){
+                    fail("Exported node type definition was not successfully read back in. "
+                            + ntd2.getName() + "differs from original");
+                }
+            }
+        }
+    }
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\compact\CompactNodeTypeDefTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NumericConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NumericConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NumericConstraintTest.java	(revision 0)
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import javax.jcr.NamespaceException;
+
+/**
+ * <code>NumericConstraintTest</code>...
+ */
+public class NumericConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(NumericConstraintTest.class);
+
+    protected ValueConstraint createInvalidConstraint() throws RepositoryException {
+        return new NumericConstraint("test");
+    }
+
+    protected ValueConstraint createValueConstraint(String definition) throws RepositoryException {
+        return new NumericConstraint(definition);
+    }
+
+    protected ValueConstraint createValueConstraint() throws RepositoryException {
+        return new NumericConstraint("(25, 48.5)");
+    }
+
+    protected int getType() {
+        return PropertyType.DOUBLE;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() throws NamespaceException, IllegalNameException, MalformedPathException {
+        return new String[] {"test", resolver.getQPath("/a/b/jcr:c").getString(), "true"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        return new String[] {"(25, 48.5)", "[0,27)", "(, 74)", "(73, 74.9]"};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return getDefinitions();
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        QValue v = valueFactory.create(75);
+        return new QValue[] {v, v, v, v};
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create("abc", PropertyType.STRING);
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\NumericConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/TestAll.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/TestAll.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/TestAll.java	(revision 0)
@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import junit.framework.TestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all test cases for compact node type tools.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("Node Type Tests");
+
+        suite.addTestSuite(BooleanConstraintTest.class);
+        suite.addTestSuite(DateConstraintTest.class);
+        suite.addTestSuite(NameConstraintTest.class);
+        suite.addTestSuite(NumericConstraintTest.class);
+        suite.addTestSuite(PathConstraintTest.class);
+        suite.addTestSuite(ReferenceConstraintTest.class);
+        suite.addTestSuite(StringConstraintTest.class);
+
+        return suite;
+    }
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\TestAll.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/StringConstraintTest.java
===================================================================
--- jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/StringConstraintTest.java	(revision 0)
+++ jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/StringConstraintTest.java	(revision 0)
@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.jackrabbit.spi.commons.nodetype;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import javax.jcr.NamespaceException;
+
+/**
+ * <code>StringConstraintTest</code>...
+ */
+public class StringConstraintTest extends ValueConstraintTest {
+
+    private static Logger log = LoggerFactory.getLogger(StringConstraintTest.class);
+
+    protected ValueConstraint createInvalidConstraint() throws RepositoryException {
+        return new StringConstraint("[abc");
+    }
+
+    protected ValueConstraint createValueConstraint(String definition) throws RepositoryException {
+        return new StringConstraint(definition);
+    }
+
+    protected ValueConstraint createValueConstraint() throws RepositoryException {
+        return new StringConstraint("[abc] constraint");
+    }
+
+    protected int getType() {
+        return PropertyType.STRING;
+    }
+
+    protected String[] getInvalidQualifiedDefinitions() throws NamespaceException, IllegalNameException, MalformedPathException {
+        return new String[] {"[abc"};
+    }
+
+    protected String[] getDefinitions() throws RepositoryException {
+        return new String[] {"[abc] constraint", "abc"};
+    }
+
+    protected String[] getQualifiedDefinitions() throws RepositoryException {
+        return getDefinitions();
+    }
+
+    protected QValue[] createNonMatchingValues() throws RepositoryException {
+        QValue v = valueFactory.create("another string", PropertyType.STRING);
+        return new QValue[] {v,v};
+    }
+
+    protected QValue createOtherValueType() throws RepositoryException {
+        return valueFactory.create(23);
+    }
+
+    // TODO: add more
+}

Property changes on: jackrabbit-spi-commons\src\test\java\org\apache\jackrabbit\spi\commons\nodetype\StringConstraintTest.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/test/resources/cnd-reader-test-input.cnd
===================================================================
--- jackrabbit-spi-commons/src/test/resources/cnd-reader-test-input.cnd	(revision 0)
+++ jackrabbit-spi-commons/src/test/resources/cnd-reader-test-input.cnd	(revision 0)
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+<ex = "http://example.org/jackrabbit/example">
+<rep='internal'>
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+
+//------------------------------------------------------------------------------
+// E X A M P L E  T Y P E S
+//------------------------------------------------------------------------------
+
+[ex:NodeType] > ex:ParentNodeType1, ex:ParentNodeType2
+  orderable mixin
+  - ex:property (long) = '1', '2' primary mandatory autocreated protected multiple version < '[1,10]'
+  + ex:node (ex:RequiredNodeType1, ex:RequiredNodeType2) = ex:RequiredNodeType1 mandatory autocreated protected multiple version
+
+[ex:AnotherNodeType] > ex:NodeType
+  - * (string) = 'a residual property' multiple
+  + * (ex:RequiredNodeType1) multiple
+  
+[ex:Empty]
+
+[ex:Reference]
+  - ex:ref (reference) mandatory protected < 'ex:ref'
+  
+[ex:Name]
+  - ex:name (name) < 'ex:name'
+  
+[ex:Path]
+  - ex:path (path) < 'ex:a/ex:b'  
+
+//------------------------------------------------------------------------------
+// B A S E  T Y P E S
+//------------------------------------------------------------------------------
+
+[nt:base]
+  - jcr:primaryType (name) mandatory autocreated protected compute
+  - jcr:mixinTypes (name) protected multiple compute
+
+[nt:unstructured]
+  orderable
+  - * (undefined) multiple
+  - * (undefined)
+  + * (nt:base) = nt:unstructured multiple version
+
+[mix:referenceable]
+  mixin
+  - jcr:uuid (string) mandatory autocreated protected initialize
+
+[mix:lockable]
+  mixin
+  - jcr:lockOwner (string) protected ignore
+  - jcr:lockIsDeep (boolean) protected ignore
+
+//------------------------------------------------------------------------------
+// V E R S I O N I N G
+//------------------------------------------------------------------------------
+
+[mix:versionable] > mix:referenceable
+  mixin
+  - jcr:versionHistory (reference) mandatory protected
+    < 'nt:versionHistory'
+  - jcr:baseVersion (reference) mandatory protected ignore
+    < 'nt:version'
+  - jcr:isCheckedOut (boolean) = 'true' mandatory autocreated protected ignore
+  - jcr:predecessors (reference) mandatory protected multiple
+    < 'nt:version'
+  - jcr:mergeFailed (reference) protected multiple abort
+
+[nt:versionHistory] > mix:referenceable
+  - jcr:versionableUuid (string) mandatory autocreated protected abort
+  + jcr:rootVersion (nt:version) = nt:version mandatory autocreated protected abort
+  + jcr:versionLabels (nt:versionLabels) = nt:versionLabels mandatory autocreated protected abort
+  + * (nt:version) = nt:version protected abort
+
+[nt:versionLabels]
+  - * (reference) protected abort
+    < 'nt:version'
+
+[nt:version] > mix:referenceable
+  - jcr:created (date) mandatory autocreated protected abort
+  - jcr:predecessors (reference) protected multiple abort
+    < 'nt:version'
+  - jcr:successors (reference) protected multiple abort
+    < 'nt:version'
+  + jcr:frozenNode (nt:frozenNode) protected abort
+
+[nt:frozenNode] > mix:referenceable
+  orderable
+  - jcr:frozenPrimaryType (name) mandatory autocreated protected abort
+  - jcr:frozenMixinTypes (name) protected multiple abort
+  - jcr:frozenUuid (string) mandatory autocreated protected abort
+  - * (undefined) protected abort
+  - * (undefined) protected multiple abort
+  + * (nt:base) protected multiple abort
+
+[nt:versionedChild]
+  - jcr:childVersionHistory (reference) mandatory autocreated protected abort
+    < 'nt:versionHistory'
+
+//------------------------------------------------------------------------------
+// N O D E T Y P E S
+//------------------------------------------------------------------------------
+
+[nt:nodeType]
+  - jcr:nodeTypeName (name) mandatory
+  - jcr:supertypes (name) multiple
+  - jcr:isMixin (boolean) mandatory
+  - jcr:hasOrderableChildNodes (boolean) mandatory
+  - jcr:primaryItemName (name)
+  + jcr:propertyDefinition (nt:propertyDefinition) = nt:propertyDefinition multiple version
+  + jcr:childNodeDefinition (nt:childNodeDefinition) = nt:childNodeDefinition multiple version
+
+[nt:propertyDefinition]
+  - jcr:name (name)
+  - jcr:autoCreated (boolean) mandatory
+  - jcr:mandatory (boolean) mandatory
+  - jcr:onParentVersion (string) mandatory
+    < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+  - jcr:protected (boolean) mandatory
+  - jcr:requiredType (string) mandatory
+    < 'STRING', 'BINARY', 'LONG', 'DOUBLE', 'BOOLEAN', 'DATE', 'NAME', 'PATH', 'REFERENCE', 'UNDEFINED'
+  - jcr:valueConstraints (string) multiple
+  - jcr:defaultValues (undefined) multiple
+  - jcr:multiple (boolean) mandatory
+
+[nt:childNodeDefinition]
+  - jcr:name (name)
+  - jcr:autoCreated (boolean) mandatory
+  - jcr:mandatory (boolean) mandatory
+  - jcr:onParentVersion (string) mandatory
+    < 'COPY', 'VERSION', 'INITIALIZE', 'COMPUTE', 'IGNORE', 'ABORT'
+  - jcr:protected (boolean) mandatory
+  - jcr:requiredPrimaryTypes (name) = 'nt:base' mandatory multiple
+  - jcr:defaultPrimaryType (name)
+  - jcr:sameNameSiblings (boolean) mandatory
+
+//------------------------------------------------------------------------------
+// M I S C
+//------------------------------------------------------------------------------
+
+[nt:hierarchyNode]
+  - jcr:created (date) autocreated protected initialize
+
+[nt:folder] > nt:hierarchyNode
+  + * (nt:hierarchyNode) version
+
+[nt:file] > nt:hierarchyNode
+  + jcr:content (nt:base) primary mandatory
+
+[nt:linkedFile] > nt:hierarchyNode
+  - jcr:content (reference) primary mandatory
+
+[nt:resource] > mix:referenceable
+  - jcr:encoding (string)
+  - jcr:mimeType (string) mandatory
+  - jcr:data (binary) primary mandatory
+  - jcr:lastModified (date) mandatory ignore
+
+[nt:query]
+  - jcr:statement (string)
+  - jcr:language (string)
+
+//------------------------------------------------------------------------------
+// J A C K R A B B I T   I N T E R N A L S
+//------------------------------------------------------------------------------
+
+[rep:nodeTypes]
+  + * (nt:nodeType) = nt:nodeType protected abort
+
+[rep:root] > nt:unstructured
+  orderable
+  + jcr:system (rep:system) = rep:system mandatory ignore
+
+[rep:system]
+  orderable
+  + jcr:versionStorage (rep:versionStorage) = rep:versionStorage mandatory protected abort
+  + jcr:nodeTypes (rep:nodeTypes) = rep:nodeTypes mandatory protected abort
+  + * (nt:base) = nt:unstructured multiple ignore
+
+[rep:versionStorage]
+  + * (nt:versionHistory) = nt:versionHistory protected multiple abort
+  + * (rep:versionStorage) = rep:versionStorage protected multiple abort
\ No newline at end of file
Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java	(revision 0)
@@ -0,0 +1,621 @@
+/*
+ * 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.spi.commons.nodetype;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QItemDefinition;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+
+/**
+ * A <code>NodeTypeDefDiff</code> represents the result of the comparison of
+ * two node type definitions.
+ * <p/>
+ * The result of the comparison can be categorized as one of the following types:
+ * <p/>
+ * <b><code>NONE</code></b> inidcates that there is no modification at all.
+ * <p/>
+ * A <b><code>TRIVIAL</code></b> modification has no impact on the consistency
+ * of existing content and does not affect existing/assigned definition id's.
+ * The following modifications are considered <code>TRIVIAL</code>:
+ * <ul>
+ * <li>changing node type <code>orderableChildNodes</code> flag
+ * <li>changing node type <code>primaryItemName</code> value
+ * <li>adding non-<code>mandatory</code> property/child node
+ * <li>changing property/child node <code>protected</code> flag
+ * <li>changing property/child node <code>onParentVersion</code> value
+ * <li>changing property/child node <code>mandatory</code> flag to <code>false</code>
+ * <li>changing property/child node <code>autoCreated</code> flag
+ * <li>changing child node <code>defaultPrimaryType</code>
+ * <li>changing child node <code>sameNameSiblings</code> flag to <code>true</code>
+ * <li>weaken property <code>valueConstraints</code> (e.g. by removing completely
+ * or by adding to existing or by making a single constraint less restrictive)
+ * <li>changing property <code>defaultValues</code>
+ * </ul>
+ * <p/>
+ * A <b><code>MINOR</code></b> modification has no impact on the consistency
+ * of existing content but <i>does</i> affect existing/assigned definition id's.
+ * The following modifications are considered <code>MINOR</code>:
+ * <ul>
+ * <li>changing specific property/child node <code>name</code> to <code>*</code>
+ * <li>weaken child node <code>requiredPrimaryTypes</code> (e.g. by removing)
+ * <li>changing specific property <code>requiredType</code> to <code>undefined</code>
+ * <li>changing property <code>multiple</code> flag to <code>true</code>
+ * </ul>
+ * <p/>
+ * A <b><code>MAJOR</code></b> modification <i>affects</i> the consistency of
+ * existing content and <i>does</i> change existing/assigned definition id's.
+ * All modifications that are neither <b><code>TRIVIAL</code></b> nor
+ * <b><code>MINOR</code></b> are considered <b><code>MAJOR</code></b>.
+ *
+ * @see #getType()
+ */
+public class NodeTypeDefDiff {
+
+    /**
+     * no modification
+     */
+    public static final int NONE = 0;
+    /**
+     * trivial modification: does neither affect consistency of existing content
+     * nor does it change existing/assigned definition id's
+     */
+    public static final int TRIVIAL = 1;
+    /**
+     * minor modification: does not affect consistency of existing content but
+     * <i>does</i> change existing/assigned definition id's
+     */
+    public static final int MINOR = 2;
+    /**
+     * major modification: <i>does</i> affect consistency of existing content
+     * and <i>does</i> change existing/assigned definition id's
+     */
+    public static final int MAJOR = 3;
+
+    private final QNodeTypeDefinition oldDef;
+    private final QNodeTypeDefinition newDef;
+    private int type;
+
+    private final List propDefDiffs = new ArrayList();
+    private final List childNodeDefDiffs = new ArrayList();
+
+    /**
+     * Constructor
+     */
+    private NodeTypeDefDiff(QNodeTypeDefinition oldDef, QNodeTypeDefinition newDef) {
+        this.oldDef = oldDef;
+        this.newDef = newDef;
+        init();
+    }
+
+    /**
+     *
+     */
+    private void init() {
+        if (oldDef.equals(newDef)) {
+            // definitions are identical
+            type = NONE;
+        } else {
+            // definitions are not identical, determine type of modification
+
+            // assume TRIVIAL change by default
+            type = TRIVIAL;
+
+            // check supertypes
+            int tmpType = supertypesDiff();
+            if (tmpType > type) {
+                type = tmpType;
+            }
+
+            // check mixin flag (MAJOR modification)
+            tmpType = mixinFlagDiff();
+            if (tmpType > type) {
+                type = tmpType;
+            }
+
+            // no need to check orderableChildNodes flag (TRIVIAL modification)
+
+            // check property definitions
+            tmpType = buildPropDefDiffs();
+            if (tmpType > type) {
+                type = tmpType;
+            }
+
+            // check child node definitions
+            tmpType = buildChildNodeDefDiffs();
+            if (tmpType > type) {
+                type = tmpType;
+            }
+        }
+    }
+
+    /**
+     * @param oldDef
+     * @param newDef
+     * @return
+     */
+    public static NodeTypeDefDiff create(QNodeTypeDefinition oldDef, QNodeTypeDefinition newDef) {
+        if (oldDef == null || newDef == null) {
+            throw new IllegalArgumentException("arguments can not be null");
+        }
+        if (!oldDef.getName().equals(newDef.getName())) {
+            throw new IllegalArgumentException("at least node type names must be matching");
+        }
+        return new NodeTypeDefDiff(oldDef, newDef);
+    }
+
+    /**
+     * @return
+     */
+    public boolean isModified() {
+        return type != NONE;
+    }
+
+    /**
+     * @return
+     */
+    public boolean isTrivial() {
+        return type == TRIVIAL;
+    }
+
+    /**
+     * @return
+     */
+    public boolean isMinor() {
+        return type == MINOR;
+    }
+
+    /**
+     * @return
+     */
+    public boolean isMajor() {
+        return type == MAJOR;
+    }
+
+    /**
+     * Returns the type of modification as expressed by the following constants:
+     * <ul>
+     * <li><b><code>NONE</code></b>: no modification at all
+     * <li><b><code>TRIVIAL</code></b>: does neither affect consistency of
+     * existing content nor does it change existing/assigned definition id's
+     * <li><b><code>MINOR</code></b>: does not affect consistency of existing
+     * content but <i>does</i> change existing/assigned definition id's
+     * <li><b><code>MAJOR</code></b>: <i>does</i> affect consistency of existing
+     * content and <i>does</i> change existing/assigned definition id's
+     * </ul>
+     *
+     * @return the type of modification
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * @return
+     */
+    public int mixinFlagDiff() {
+        return oldDef.isMixin() != newDef.isMixin() ? MAJOR : NONE;
+    }
+
+    /**
+     * @return
+     */
+    public int supertypesDiff() {
+        return !Arrays.equals(oldDef.getSupertypes(), newDef.getSupertypes()) ? MAJOR : NONE;
+    }
+
+    /**
+     * @return
+     */
+    private int buildPropDefDiffs() {
+        /**
+         * propDefId determinants: declaringNodeType, name, requiredType, multiple
+         * todo: try also to match entries with modified id's
+         */
+
+        int maxType = NONE;
+        QPropertyDefinition[] pda1 = oldDef.getPropertyDefs();
+        HashMap defs1 = new HashMap();
+        for (int i = 0; i < pda1.length; i++) {
+            defs1.put(pda1[i].getName(), pda1[i]);
+        }
+
+        QPropertyDefinition[] pda2 = newDef.getPropertyDefs();
+        HashMap defs2 = new HashMap();
+        for (int i = 0; i < pda2.length; i++) {
+            defs2.put(pda2[i].getName(), pda2[i]);
+        }
+
+        /**
+         * walk through defs1 and process all entries found in
+         * both defs1 & defs2 and those found only in defs1
+         */
+        Iterator iter = defs1.keySet().iterator();
+        while (iter.hasNext()) {
+            Name name = (Name) iter.next();
+            QPropertyDefinition def1 = (QPropertyDefinition) defs1.get(name);
+            QPropertyDefinition def2 = (QPropertyDefinition) defs2.get(name);
+            PropDefDiff diff = new PropDefDiff(def1, def2);
+            if (diff.getType() > maxType) {
+                maxType = diff.getType();
+            }
+            propDefDiffs.add(diff);
+            defs2.remove(name);
+        }
+
+        /**
+         * defs2 by now only contains entries found in defs2 only;
+         * walk through defs2 and process all remaining entries
+         */
+        iter = defs2.keySet().iterator();
+        while (iter.hasNext()) {
+            Name name = (Name) iter.next();
+            QPropertyDefinition def = (QPropertyDefinition) defs2.get(name);
+            PropDefDiff diff = new PropDefDiff(null, def);
+            if (diff.getType() > maxType) {
+                maxType = diff.getType();
+            }
+            propDefDiffs.add(diff);
+        }
+
+        return maxType;
+    }
+
+    /**
+     * @return
+     */
+    private int buildChildNodeDefDiffs() {
+        /**
+         * nodeDefId determinants: declaringNodeType, name, requiredPrimaryTypes
+         * todo: try also to match entries with modified id's
+         */
+
+        int maxType = NONE;
+        QNodeDefinition[] cnda1 = oldDef.getChildNodeDefs();
+        HashMap defs1 = new HashMap();
+        for (int i = 0; i < cnda1.length; i++) {
+            defs1.put(cnda1[i].getName(), cnda1[i]);
+        }
+
+        QNodeDefinition[] cnda2 = newDef.getChildNodeDefs();
+        HashMap defs2 = new HashMap();
+        for (int i = 0; i < cnda2.length; i++) {
+            defs2.put(cnda2[i].getName(), cnda2[i]);
+        }
+
+        /**
+         * walk through defs1 and process all entries found in
+         * both defs1 & defs2 and those found only in defs1
+         */
+        Iterator iter = defs1.keySet().iterator();
+        while (iter.hasNext()) {
+            Name name = (Name) iter.next();
+            QNodeDefinition def1 = (QNodeDefinition) defs1.get(name);
+            QNodeDefinition def2 = (QNodeDefinition) defs2.get(name);
+            ChildNodeDefDiff diff = new ChildNodeDefDiff(def1, def2);
+            if (diff.getType() > maxType) {
+                maxType = diff.getType();
+            }
+            childNodeDefDiffs.add(diff);
+            defs2.remove(name);
+        }
+
+        /**
+         * defs2 by now only contains entries found in defs2 only;
+         * walk through defs2 and process all remaining entries
+         */
+        iter = defs2.keySet().iterator();
+        while (iter.hasNext()) {
+            Name name = (Name) iter.next();
+            QNodeDefinition def = (QNodeDefinition) defs2.get(name);
+            ChildNodeDefDiff diff = new ChildNodeDefDiff(null, def);
+            if (diff.getType() > maxType) {
+                maxType = diff.getType();
+            }
+            childNodeDefDiffs.add(diff);
+        }
+
+        return maxType;
+    }
+
+    public String toString() {
+        String result = getClass().getName() + "[\n\tnodeTypeName="
+                + oldDef.getName();
+
+        result += ",\n\tmixinFlagDiff=" + modificationTypeToString(mixinFlagDiff());
+        result += ",\n\tsupertypesDiff=" + modificationTypeToString(supertypesDiff());
+
+        result += ",\n\tpropertyDifferences=[\n";
+        result += toString(propDefDiffs);
+        result += "\t]";
+
+        result += ",\n\tchildNodeDifferences=[\n";
+        result += toString(childNodeDefDiffs);
+        result += "\t]\n";
+        result += "]\n";
+
+        return result;
+    }
+
+    private String toString(List childItemDefDiffs) {
+        String result = "";
+        for (Iterator iter = childItemDefDiffs.iterator(); iter.hasNext();) {
+            ChildItemDefDiff propDefDiff = (ChildItemDefDiff) iter.next();
+            result += "\t\t" + propDefDiff;
+            if (iter.hasNext()) {
+                result += ",";
+            }
+            result += "\n";
+        }
+        return result;
+    }
+
+    private String modificationTypeToString(int modifcationType) {
+        String typeString = "unknown";
+        switch (modifcationType) {
+            case NONE:
+                typeString = "NONE";
+                break;
+            case TRIVIAL:
+                typeString = "TRIVIAL";
+                break;
+            case MINOR:
+                typeString = "MINOR";
+                break;
+            case MAJOR:
+                typeString = "MAJOR";
+                break;
+        }
+        return typeString;
+    }
+
+
+    //--------------------------------------------------------< inner classes >
+
+    abstract class ChildItemDefDiff {
+        protected final QItemDefinition oldDef;
+        protected final QItemDefinition newDef;
+        protected int type;
+
+        ChildItemDefDiff(QItemDefinition oldDef, QItemDefinition newDef) {
+            this.oldDef = oldDef;
+            this.newDef = newDef;
+            init();
+        }
+
+        protected void init() {
+            // determine type of modification
+            if (isAdded()) {
+                if (!newDef.isMandatory()) {
+                    // adding a non-mandatory child item is a TRIVIAL change
+                    type = TRIVIAL;
+                } else {
+                    // adding a mandatory child item is a MAJOR change
+                    type = MAJOR;
+                }
+            } else if (isRemoved()) {
+                // removing a child item is a MAJOR change
+                type = MAJOR;
+            } else {
+                /**
+                 * neither added nor removed => has to be either identical
+                 * or modified
+                 */
+                if (oldDef.equals(newDef)) {
+                    // identical
+                    type = NONE;
+                } else {
+                    // modified
+                    if (oldDef.isMandatory() != newDef.isMandatory()
+                            && newDef.isMandatory()) {
+                        // making a child item mandatory is a MAJOR change
+                        type = MAJOR;
+                    } else {
+                        if (!oldDef.definesResidual()
+                                && newDef.definesResidual()) {
+                            // just making a child item residual is a MINOR change
+                            type = MINOR;
+                        } else {
+                            if (!oldDef.getName().equals(newDef.getName())) {
+                                // changing the name of a child item is a MAJOR change
+                                type = MAJOR;
+                            } else {
+                                // all other changes are TRIVIAL
+                                type = TRIVIAL;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public boolean isAdded() {
+            return oldDef == null && newDef != null;
+        }
+
+        public boolean isRemoved() {
+            return oldDef != null && newDef == null;
+        }
+
+        public boolean isModified() {
+            return oldDef != null && newDef != null
+                    && !oldDef.equals(newDef);
+        }
+
+        public String toString() {
+            String typeString = modificationTypeToString(getType());
+
+            String operationString;
+            if (isAdded()) {
+                operationString = "ADDED";
+            } else if (isModified()) {
+                operationString = "MODIFIED";
+            } else if (isRemoved()) {
+                operationString = "REMOVED";
+            } else {
+                operationString = "NONE";
+            }
+
+            QItemDefinition itemDefinition = (oldDef != null) ? oldDef : newDef;
+
+            return getClass().getName() + "[itemName="
+                    + itemDefinition.getName() + ", type=" + typeString
+                    + ", operation=" + operationString + "]";
+        }
+
+    }
+
+    public class PropDefDiff extends ChildItemDefDiff {
+
+        PropDefDiff(QPropertyDefinition oldDef, QPropertyDefinition newDef) {
+            super(oldDef, newDef);
+        }
+
+        public QPropertyDefinition getOldDef() {
+            return (QPropertyDefinition) oldDef;
+        }
+
+        public QPropertyDefinition getNewDef() {
+            return (QPropertyDefinition) newDef;
+        }
+
+        protected void init() {
+            super.init();
+            /**
+             * only need to do comparison if base class implementation
+             * detected a non-MAJOR modification (i.e. TRIVIAL or MINOR);
+             * no need to check for additions or removals as this is already
+             * handled in base class implementation.
+             */
+            if (isModified() && type != NONE && type != MAJOR) {
+                /**
+                 * check if valueConstraints were made more restrictive
+                 * (constraints are ORed)
+                 */
+                String[] vca1 = getOldDef().getValueConstraints();
+                HashSet set1 = new HashSet();
+                for (int i = 0; i < vca1.length; i++) {
+                    set1.add(vca1[i]);
+                }
+                String[] vca2 = getNewDef().getValueConstraints();
+                HashSet set2 = new HashSet();
+                for (int i = 0; i < vca2.length; i++) {
+                    set2.add(vca2[i]);
+                }
+
+                if (set1.isEmpty() && !set2.isEmpty()) {
+                    // added constraint where there was no constraint (MAJOR change)
+                    type = MAJOR;
+                } else if (!set2.containsAll(set1) && !set2.isEmpty()) {
+                    // removed existing constraint (MAJOR change)
+                    type = MAJOR;
+                }
+
+                // no need to check defaultValues (TRIVIAL change)
+
+                if (type == TRIVIAL) {
+                    int t1 = getOldDef().getRequiredType();
+                    int t2 = getNewDef().getRequiredType();
+                    if (t1 != t2) {
+                        if (t2 == PropertyType.UNDEFINED) {
+                            // changed getRequiredType to UNDEFINED (MINOR change)
+                            type = MINOR;
+                        } else {
+                            // changed getRequiredType to specific type (MAJOR change)
+                            type = MAJOR;
+                        }
+                    }
+                    boolean b1 = getOldDef().isMultiple();
+                    boolean b2 = getNewDef().isMultiple();
+                    if (b1 != b2) {
+                        if (b2) {
+                            // changed multiple flag to true (MINOR change)
+                            type = MINOR;
+                        } else {
+                            // changed multiple flag to false (MAJOR change)
+                            type = MAJOR;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public class ChildNodeDefDiff extends ChildItemDefDiff {
+
+        ChildNodeDefDiff(QNodeDefinition oldDef, QNodeDefinition newDef) {
+            super(oldDef, newDef);
+        }
+
+        public QNodeDefinition getOldDef() {
+            return (QNodeDefinition) oldDef;
+        }
+
+        public QNodeDefinition getNewDef() {
+            return (QNodeDefinition) newDef;
+        }
+
+        protected void init() {
+            super.init();
+            /**
+             * only need to do comparison if base class implementation
+             * detected a non-MAJOR modification (i.e. TRIVIAL or MINOR);
+             * no need to check for additions or removals as this is already
+             * handled in base class implementation.
+             */
+            if (isModified() && type != NONE && type != MAJOR) {
+
+                boolean b1 = getOldDef().allowsSameNameSiblings();
+                boolean b2 = getNewDef().allowsSameNameSiblings();
+                if (b1 != b2 && !b2) {
+                    // changed sameNameSiblings flag to false (MAJOR change)
+                    type = MAJOR;
+                }
+
+                // no need to check defaultPrimaryType (TRIVIAL change)
+
+                if (type == TRIVIAL) {
+                    List l1 = Arrays.asList(getOldDef().getRequiredPrimaryTypes());
+                    List l2 = Arrays.asList(getNewDef().getRequiredPrimaryTypes());
+                    if (!l1.equals(l2)) {
+                        if (l1.containsAll(l2)) {
+                            // removed requiredPrimaryType (MINOR change)
+                            type = MINOR;
+                        } else {
+                            // added requiredPrimaryType (MAJOR change)
+                            type = MAJOR;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\NodeTypeDefDiff.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java	(revision 0)
@@ -0,0 +1,909 @@
+/*
+ * 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.spi.commons.nodetype;
+
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.value.DateValue;
+import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+import javax.jcr.NamespaceException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import java.util.Calendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * <code>ValueConstraint</code> and its subclasses are used to check the
+ * syntax of a value constraint and to test if a specific value satisfies
+ * it.
+ */
+public abstract class ValueConstraint {
+
+    protected static Logger log = LoggerFactory.getLogger(ValueConstraint.class);
+
+    public static final ValueConstraint[] EMPTY_ARRAY = new ValueConstraint[0];
+
+    // TODO improve. don't rely on a specific factory impl
+    static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+
+    private final String qualifiedDefinition;
+
+    protected ValueConstraint(String qualifiedDefinition) {
+        this.qualifiedDefinition = qualifiedDefinition;
+    }
+
+    /**
+     * For constraints that are not namespace prefix mapping sensitive this
+     * method returns the same result as <code>{@link #getQualifiedDefinition()}</code>.
+     * <p/>
+     * Those that are namespace prefix mapping sensitive (e.g.
+     * <code>NameConstraint</code>, <code>PathConstraint</code> and
+     * <code>ReferenceConstraint</code>) use the given <code>nsResolver</code>
+     * to reflect the current mapping in the returned value.
+     * In other words: subclasses, that need to make a conversion to JCR value
+     * must overwrite this and return a value that has all qualified names
+     * and path elements resolved.
+     *
+     * @return the definition of this constraint.
+     * @see #getQualifiedDefinition()
+     * @param resolver
+     */
+    public String getDefinition(NamePathResolver resolver) {
+        return qualifiedDefinition;
+    }
+
+    /**
+     * By default the qualified definition is the same as the JCR definition.
+     *
+     * @return the qualified definition String
+     * @see #getDefinition(NamePathResolver)
+     */
+    public String getQualifiedDefinition() {
+        return qualifiedDefinition;
+    }
+
+    /**
+     * Check if the specified value matches the this constraint.
+     *
+     * @param value The value to be tested.
+     * @throws ConstraintViolationException If the specified value is
+     * <code>null</code> or does not matches the constraint.
+     * @throws RepositoryException If another error occurs.
+     */
+    abstract void check(QValue value) throws ConstraintViolationException, RepositoryException;
+
+    //-----------------------------------------< java.lang.Object overrides >---
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        } else if (other instanceof ValueConstraint) {
+            return qualifiedDefinition.equals(((ValueConstraint) other).qualifiedDefinition);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the hashCode of the definition String
+     *
+     * @return the hashCode of the definition String
+     * @see Object#hashCode()
+     */
+    public int hashCode() {
+        return qualifiedDefinition.hashCode();
+    }
+
+    //-----------------------------------< static factory and check methods >---
+    /**
+     * Create a new <code>ValueConstraint</code> from the String representation.
+     * Note, that the definition must be in the qualified format in case the type
+     * indicates {@link PropertyType#NAME}, {@link PropertyType#PATH} or {@link PropertyType#REFERENCE}
+     *
+     * @param type
+     * @param qualifiedDefinition
+     * @return
+     * @throws InvalidConstraintException
+     */
+    public static ValueConstraint create(int type, String qualifiedDefinition)
+        throws InvalidConstraintException {
+        if (qualifiedDefinition == null) {
+            throw new IllegalArgumentException("illegal definition (null)");
+        }
+        switch (type) {
+            // constraints which are not qName senstive
+            case PropertyType.STRING:
+                return new StringConstraint(qualifiedDefinition);
+
+            case PropertyType.BOOLEAN:
+                return new BooleanConstraint(qualifiedDefinition);
+
+            case PropertyType.BINARY:
+                return new NumericConstraint(qualifiedDefinition);
+
+            case PropertyType.DATE:
+                return new DateConstraint(qualifiedDefinition);
+
+            case PropertyType.LONG:
+            case PropertyType.DOUBLE:
+                return new NumericConstraint(qualifiedDefinition);
+
+            // qName sensitive constraints: create from qualified string
+            case PropertyType.NAME:
+                return NameConstraint.create(qualifiedDefinition);
+
+            case PropertyType.PATH:
+                return PathConstraint.create(qualifiedDefinition);
+
+            case PropertyType.REFERENCE:
+                return ReferenceConstraint.create(qualifiedDefinition);
+
+            default:
+                throw new IllegalArgumentException("unknown/unsupported target type for constraint: "
+                        + PropertyType.nameFromValue(type));
+        }
+    }
+
+    /**
+     *
+     * @param type
+     * @param definition
+     * @param resolver
+     * @return
+     * @throws InvalidConstraintException
+     */
+    public static ValueConstraint create(int type, String definition,
+                                         NamePathResolver resolver)
+            throws InvalidConstraintException {
+        if (definition == null) {
+            throw new IllegalArgumentException("Illegal definition (null) for ValueConstraint.");
+        }
+        switch (type) {
+            case PropertyType.STRING:
+                return new StringConstraint(definition);
+
+            case PropertyType.BOOLEAN:
+                return new BooleanConstraint(definition);
+
+            case PropertyType.BINARY:
+                return new NumericConstraint(definition);
+
+            case PropertyType.DATE:
+                return new DateConstraint(definition);
+
+            case PropertyType.LONG:
+            case PropertyType.DOUBLE:
+                return new NumericConstraint(definition);
+
+            case PropertyType.NAME:
+                return NameConstraint.create(definition, resolver);
+
+            case PropertyType.PATH:
+                return PathConstraint.create(definition, resolver);
+
+            case PropertyType.REFERENCE:
+                return ReferenceConstraint.create(definition, resolver);
+
+            default:
+                throw new IllegalArgumentException("Unknown/unsupported target type for constraint: " + PropertyType.nameFromValue(type));
+        }
+    }
+
+    /**
+     * Tests if the value constraints defined in the property definition
+     * <code>pd</code> are satisfied by the the specified <code>values</code>.
+     * <p/>
+     * Note that the <i>protected</i> flag is not checked. Also note that no
+     * type conversions are attempted if the type of the given values does not
+     * match the required type as specified in the given definition.
+     *
+     * @param pd
+     * @param values
+     * @throws ConstraintViolationException
+     */
+    public static void checkValueConstraints(QPropertyDefinition pd, QValue[] values)
+            throws ConstraintViolationException, RepositoryException {
+        // check multi-value flag
+        if (!pd.isMultiple() && values != null && values.length > 1) {
+            throw new ConstraintViolationException("the property is not multi-valued");
+        }
+
+        String[] constraints = pd.getValueConstraints();
+        if (constraints == null || constraints.length == 0) {
+            // no constraints to check
+            return;
+        }
+        if (values != null && values.length > 0) {
+            // check value constraints on every value
+            for (int i = 0; i < values.length; i++) {
+                // constraints are OR-ed together
+                boolean satisfied = false;
+                ConstraintViolationException cve = null;
+                for (int j = 0; j < constraints.length && !satisfied; j++) {
+                    try {
+                        ValueConstraint cnstr = ValueConstraint.create(pd.getRequiredType(), constraints[j]);
+                        cnstr.check(values[i]);
+                        satisfied = true;
+                    } catch (ConstraintViolationException e) {
+                        cve = e;
+                    } catch (InvalidConstraintException e) {
+                        cve = new ConstraintViolationException(e.getMessage(), e);
+                    }
+                }
+                if (!satisfied) {
+                    // re-throw last exception we encountered
+                    throw cve;
+                }
+            }
+        }
+    }
+}
+
+//---------------------------------------------< Subclass BooleanConstraint >---
+/**
+ * <code>BooleanConstraint</code> ...
+ */
+class BooleanConstraint extends ValueConstraint {
+    final boolean reqBool;
+
+    BooleanConstraint(String definition) throws InvalidConstraintException {
+        super(definition);
+
+        // constraint format: 'true' or 'false'
+        if (definition.equals("true")) {
+            reqBool = true;
+        } else if (definition.equals("false")) {
+            reqBool = false;
+        } else {
+            String msg = "'" + definition
+                    + "' is not a valid value constraint format for BOOLEAN values";
+            log.debug(msg);
+            throw new InvalidConstraintException(msg);
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '"  + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.BOOLEAN:
+                boolean b = Boolean.valueOf(value.getString()).booleanValue();
+                if (b != reqBool) {
+                    throw new ConstraintViolationException("'" + b + "' does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+                }
+                return;
+
+            default:
+                String msg = "BOOLEAN constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//----------------------------------------------< Subclass StringConstraint >---
+/**
+ * <code>StringConstraint</code> ...
+ */
+class StringConstraint extends ValueConstraint {
+    final Pattern pattern;
+
+    StringConstraint(String definition) throws InvalidConstraintException {
+        super(definition);
+
+        // constraint format: regexp
+        try {
+            pattern = Pattern.compile(definition);
+        } catch (PatternSyntaxException pse) {
+            String msg = "'" + definition + "' is not valid regular expression syntax";
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, pse);
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.STRING:
+                String text = value.getString();
+                Matcher matcher = pattern.matcher(text);
+                if (!matcher.matches()) {
+                    throw new ConstraintViolationException("'" + text  + "' does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+                }
+                return;
+
+            default:
+                String msg = "STRING constraint can not be applied to value of type: " + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//---------------------------------------------< Subclass NumericConstraint >---
+/**
+ * <code>NumericConstraint</code> ...
+ */
+class NumericConstraint extends ValueConstraint {
+    final boolean lowerInclusive;
+    final Double lowerLimit;
+    final boolean upperInclusive;
+    final Double upperLimit;
+
+    NumericConstraint(String definition) throws InvalidConstraintException {
+        super(definition);
+
+        // format: '(<min>, <max>)',  '[<min>, <max>]', '(, <max>)' etc.
+        Pattern pattern = Pattern.compile("([\\(\\[]) *(\\-?\\d+\\.?\\d*)? *, *(\\-?\\d+\\.?\\d*)? *([\\)\\]])");
+        Matcher matcher = pattern.matcher(definition);
+        if (matcher.matches()) {
+            try {
+                // group 1 is lower inclusive/exclusive
+                String s = matcher.group(1);
+                lowerInclusive = s.equals("[");
+                // group 2 is lower limit
+                s = matcher.group(2);
+                if (s == null || s.length() == 0) {
+                    lowerLimit = null;
+                } else {
+                    lowerLimit = Double.valueOf(matcher.group(2));
+                }
+                // group 3 is upper limit
+                s = matcher.group(3);
+                if (s == null || s.length() == 0) {
+                    upperLimit = null;
+                } else {
+                    upperLimit = Double.valueOf(matcher.group(3));
+                }
+                // group 4 is lower inclusive/exclusive
+                s = matcher.group(4);
+                upperInclusive = s.equals("]");
+                if (lowerLimit == null && upperLimit == null) {
+                    String msg = "'" + definition + "' is not a valid value constraint"
+                            + " format for numeric types: neither lower- nor upper-limit specified";
+                    log.debug(msg);
+                    throw new InvalidConstraintException(msg);
+                }
+                if (lowerLimit != null && upperLimit != null) {
+                    if (lowerLimit.doubleValue() > upperLimit.doubleValue()) {
+                        String msg = "'" + definition
+                                + "' is not a valid value constraint format for numeric types: lower-limit exceeds upper-limit";
+                        log.debug(msg);
+                        throw new InvalidConstraintException(msg);
+                    }
+                }
+            } catch (NumberFormatException nfe) {
+                String msg = "'" + definition
+                        + "' is not a valid value constraint format for numeric types";
+                log.debug(msg);
+                throw new InvalidConstraintException(msg, nfe);
+            }
+        } else {
+            String msg = "'" + definition
+                    + "' is not a valid value constraint format for numeric values";
+            log.debug(msg);
+            throw new InvalidConstraintException(msg);
+        }
+    }
+
+    private void check(double number) throws ConstraintViolationException {
+        if (lowerLimit != null) {
+            if (lowerInclusive) {
+                if (number < lowerLimit.doubleValue()) {
+                    throw new ConstraintViolationException(number
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            } else {
+                if (number <= lowerLimit.doubleValue()) {
+                    throw new ConstraintViolationException(number
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            }
+        }
+        if (upperLimit != null) {
+            if (upperInclusive) {
+                if (number > upperLimit.doubleValue()) {
+                    throw new ConstraintViolationException(number
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            } else {
+                if (number >= upperLimit.doubleValue()) {
+                    throw new ConstraintViolationException(number
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            }
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '"
+                    + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.LONG:
+                check(value.getLong());
+                return;
+
+            case PropertyType.DOUBLE:
+                check(value.getDouble());
+                return;
+
+            case PropertyType.BINARY:
+                long length = value.getLength();
+                if (length != -1) {
+                    check(length);
+                } else {
+                    log.warn("failed to determine length of binary value");
+                }
+                return;
+
+            default:
+                String msg = "numeric constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//------------------------------------------------< Subclass DateConstraint >---
+/**
+ * <code>DateConstraint</code> ...
+ */
+class DateConstraint extends ValueConstraint {
+
+    final boolean lowerInclusive;
+    final Calendar lowerLimit;
+    final boolean upperInclusive;
+    final Calendar upperLimit;
+
+    DateConstraint(String definition) throws InvalidConstraintException {
+        super(definition);
+
+        // format: '(<fromDate>, <toDate>)', '[<fromDate>, <toDate>]', '[, <toDate>]' etc.
+        Pattern pattern = Pattern.compile("([\\(\\[]) *([0-9TZ\\.\\+-:]*)? *, *([0-9TZ\\.\\+-:]*)? *([\\)\\]])");
+        Matcher matcher = pattern.matcher(definition);
+        if (matcher.matches()) {
+            try {
+                // group 1 is lower inclusive/exclusive
+                String s = matcher.group(1);
+                lowerInclusive = s.equals("[");
+                // group 2 is lower limit
+                s = matcher.group(2);
+                if (s == null || s.length() == 0) {
+                    lowerLimit = null;
+                } else {
+                    lowerLimit = DateValue.valueOf(matcher.group(2)).getDate();
+                }
+                // group 3 is upper limit
+                s = matcher.group(3);
+                if (s == null || s.length() == 0) {
+                    upperLimit = null;
+                } else {
+                    upperLimit = DateValue.valueOf(matcher.group(3)).getDate();
+                }
+                // group 4 is upepr inclusive/exclusive
+                s = matcher.group(4);
+                upperInclusive = s.equals("]");
+
+                if (lowerLimit == null && upperLimit == null) {
+                    String msg = "'" + definition
+                            + "' is not a valid value constraint format for dates: neither min- nor max-date specified";
+                    log.debug(msg);
+                    throw new InvalidConstraintException(msg);
+                }
+                if (lowerLimit != null && upperLimit != null) {
+                    if (lowerLimit.after(upperLimit)) {
+                        String msg = "'" + definition
+                                + "' is not a valid value constraint format for dates: min-date > max-date";
+                        log.debug(msg);
+                        throw new InvalidConstraintException(msg);
+                    }
+                }
+            } catch (ValueFormatException vfe) {
+                String msg = "'" + definition
+                        + "' is not a valid value constraint format for dates";
+                log.debug(msg);
+                throw new InvalidConstraintException(msg, vfe);
+            } catch (RepositoryException re) {
+                String msg = "'" + definition
+                        + "' is not a valid value constraint format for dates";
+                log.debug(msg);
+                throw new InvalidConstraintException(msg, re);
+            }
+        } else {
+            String msg = "'" + definition
+                    + "' is not a valid value constraint format for dates";
+            log.debug(msg);
+            throw new InvalidConstraintException(msg);
+        }
+    }
+
+    private void check(Calendar cal) throws ConstraintViolationException {
+        if (cal == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        if (lowerLimit != null) {
+            if (lowerInclusive) {
+                if (cal.getTimeInMillis() < lowerLimit.getTimeInMillis()) {
+                    throw new ConstraintViolationException(cal
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            } else {
+                if (cal.getTimeInMillis() <= lowerLimit.getTimeInMillis()) {
+                    throw new ConstraintViolationException(cal
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            }
+        }
+        if (upperLimit != null) {
+            if (upperInclusive) {
+                if (cal.getTimeInMillis() > upperLimit.getTimeInMillis()) {
+                    throw new ConstraintViolationException(cal
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            } else {
+                if (cal.getTimeInMillis() >= upperLimit.getTimeInMillis()) {
+                    throw new ConstraintViolationException(cal
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+            }
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.DATE:
+                check(value.getCalendar());
+                return;
+
+            default:
+                String msg = "DATE constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//------------------------------------------------< Subclass PathConstraint >---
+/**
+ * <code>PathConstraint</code> ...
+ */
+class PathConstraint extends ValueConstraint {
+
+    final Path path;
+    final boolean deep;
+
+    static PathConstraint create(String qualifiedDefinition) throws InvalidConstraintException {
+        // constraint format: qualified absolute or relative path with optional trailing wildcard
+        boolean deep = qualifiedDefinition.endsWith("*");
+        Path path;
+        // TODO improve. don't rely on a specific factory impl
+        if (deep) {
+            path = PathFactoryImpl.getInstance().create(qualifiedDefinition.substring(0, qualifiedDefinition.length() - 1));
+        } else {
+            path = PathFactoryImpl.getInstance().create(qualifiedDefinition);
+        }
+        return new PathConstraint(qualifiedDefinition, path, deep);
+    }
+
+    static PathConstraint create(String definition, PathResolver resolver)
+            throws InvalidConstraintException {
+        try {
+            StringBuffer qualifiedDefinition = new StringBuffer();
+            // constraint format: absolute or relative path with optional
+            // trailing wildcard
+            boolean deep = definition.endsWith("/*");
+            if (deep) {
+                // trim trailing wildcard before building path
+                if (definition.equals("/*")) {
+                    definition = "/";
+                    qualifiedDefinition.append('*');
+                } else {
+                    definition = definition.substring(0, definition.length() - 2);
+                    qualifiedDefinition.append("/*");
+                }
+            }
+            Path path = resolver.getQPath(definition);
+            qualifiedDefinition.insert(0, path.getString());
+
+            return new PathConstraint(qualifiedDefinition.toString(), path, deep);
+        } catch (NameException e) {
+            String msg = "Invalid path expression specified as value constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        } catch (NamespaceException e) {
+            String msg = "Invalid path expression specified as value constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        }
+    }
+
+    private PathConstraint(String qualifiedDefinition, Path path, boolean deep) throws InvalidConstraintException {
+        super(qualifiedDefinition);
+        this.path = path;
+        this.deep = deep;
+    }
+
+    /**
+     * Uses {@link NamePathResolver#getJCRPath(Path)} to convert the
+     * qualified <code>Path</code> into a JCR path.
+     *
+     * @see ValueConstraint#getDefinition(NamePathResolver)
+     * @param resolver
+     */
+    public String getDefinition(NamePathResolver resolver) {
+        try {
+            String p = resolver.getJCRPath(path);
+            if (!deep) {
+                return p;
+            } else if (path.denotesRoot()) {
+                return p + "*";
+            } else {
+                return p + "/*";
+            }
+        } catch (NamespaceException e) {
+            // should never get here, return raw definition as fallback
+            return getQualifiedDefinition();
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.PATH:
+                Path p = value.getPath();
+                // normalize paths before comparing them
+                Path p0, p1;
+                try {
+                    p0 = path.getNormalizedPath();
+                    p1 = p.getNormalizedPath();
+                } catch (RepositoryException e) {
+                    throw new ConstraintViolationException("path not valid: " + e);
+                }
+                if (deep) {
+                    try {
+                        if (!p0.isAncestorOf(p1)) {
+                            throw new ConstraintViolationException(p
+                                + " does not satisfy the constraint '"
+                                + getQualifiedDefinition() + "'");
+                        }
+                    } catch (RepositoryException e) {
+                        // can't compare relative with absolute path
+                        throw new ConstraintViolationException(p
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                    }
+                } else {
+                    // exact match required
+                    if (!p0.equals(p1)) {
+                        throw new ConstraintViolationException(p
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                    }
+                }
+                return;
+
+            default:
+                String msg = "PATH constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//------------------------------------------------< Subclass NameConstraint >---
+/**
+ * <code>NameConstraint</code> ...
+ */
+class NameConstraint extends ValueConstraint {
+
+    private final Name name;
+
+    static NameConstraint create(String qualifiedDefinition) {
+        // constraint format: String representation of qualified name
+        return new NameConstraint(qualifiedDefinition, NAME_FACTORY.create(qualifiedDefinition));
+    }
+
+    static NameConstraint create(String definition, NameResolver resolver)
+            throws InvalidConstraintException {
+        // constraint format: JCR name in prefix form
+        try {
+            Name name = resolver.getQName(definition);
+            return new NameConstraint(name.toString(), name);
+        } catch (NameException e) {
+            String msg = "Invalid name constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        } catch (NamespaceException e) {
+            String msg = "Invalid name constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        }
+    }
+
+    private NameConstraint(String qualifiedDefinition, Name name) {
+        super(qualifiedDefinition);
+        this.name = name;
+    }
+
+    /**
+     * Uses {@link NamePathResolver#getJCRName(Name)} to convert the
+     * qualified <code>Name</code> into a JCR name.
+     *
+     * @see ValueConstraint#getDefinition(NamePathResolver)
+     * @param resolver
+     */
+    public String getDefinition(NamePathResolver resolver) {
+        try {
+            return resolver.getJCRName(name);
+        } catch (NamespaceException e) {
+            // should never get here, return raw definition as fallback
+            return getQualifiedDefinition();
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.NAME:
+                Name n = value.getName();
+                if (!name.equals(n)) {
+                    throw new ConstraintViolationException(n
+                            + " does not satisfy the constraint '"
+                            + getQualifiedDefinition() + "'");
+                }
+                return;
+
+            default:
+                String msg = "NAME constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+//-------------------------------------------< Subclass ReferenceConstraint >---
+/**
+ * <code>ReferenceConstraint</code> ...
+ */
+class ReferenceConstraint extends ValueConstraint {
+
+    private final Name ntName;
+
+    static ReferenceConstraint create(String qualifiedDefinition) {
+        // constraint format: String representation of qualified name
+        return new ReferenceConstraint(qualifiedDefinition, NAME_FACTORY.create(qualifiedDefinition));
+    }
+
+    static ReferenceConstraint create(String definition, NameResolver resolver)
+            throws InvalidConstraintException {
+        // constraint format: JCR name in prefix form
+        try {
+            Name name = resolver.getQName(definition);
+            return new ReferenceConstraint(name.toString(), name);
+        } catch (NameException e) {
+            String msg = "Invalid name constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        } catch (NamespaceException e) {
+            String msg = "Invalid name constraint: " + definition;
+            log.debug(msg);
+            throw new InvalidConstraintException(msg, e);
+        }
+    }
+
+    private ReferenceConstraint(String qualifiedDefinition, Name ntName) {
+        super(qualifiedDefinition);
+        this.ntName = ntName;
+    }
+
+    /**
+     * Uses {@link NamePathResolver#getJCRName(Name)} to convert the
+     * qualified <code>Name</code> into a JCR name.
+     *
+     * @see ValueConstraint#getDefinition(NamePathResolver)
+     * @param resolver
+     */
+    public String getDefinition(NamePathResolver resolver) {
+        try {
+            return resolver.getJCRName(ntName);
+        } catch (NamespaceException e) {
+            // should never get here, return raw definition as fallback
+            return getQualifiedDefinition();
+        }
+    }
+
+    /**
+     * @see ValueConstraint#check(QValue)
+     */
+    void check(QValue value) throws ConstraintViolationException, RepositoryException {
+        if (value == null) {
+            throw new ConstraintViolationException("Null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
+        }
+        switch (value.getType()) {
+            case PropertyType.REFERENCE:
+                // TODO check REFERENCE value constraint (requires a session)
+                log.warn("validation of REFERENCE constraint is not yet implemented");
+                return;
+
+            default:
+                String msg = "REFERENCE constraint can not be applied to value of type: "
+                        + PropertyType.nameFromValue(value.getType());
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+    }
+}
+
+
+

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\ValueConstraint.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/Lexer.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/Lexer.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/Lexer.java	(revision 0)
@@ -0,0 +1,158 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import java.io.StreamTokenizer;
+import java.io.Reader;
+import java.io.IOException;
+
+/**
+ * Lexer
+ */
+public class Lexer {
+    public static final char SINGLE_QUOTE = '\'';
+    public static final char DOUBLE_QUOTE = '\"';
+    public static final char BEGIN_NODE_TYPE_NAME = '[';
+    public static final char END_NODE_TYPE_NAME = ']';
+    public static final char EXTENDS = '>';
+    public static final char LIST_DELIMITER = ',';
+    public static final char PROPERTY_DEFINITION = '-';
+    public static final char CHILD_NODE_DEFINITION = '+';
+    public static final char BEGIN_TYPE = '(';
+    public static final char END_TYPE = ')';
+    public static final char DEFAULT = '=';
+    public static final char CONSTRAINT = '<';
+
+    public static final String[] ORDERABLE = new String[] {"orderable", "ord", "o"};
+    public static final String[] MIXIN = new String[]{"mixin", "mix", "m"};
+
+    public static final String[] PRIMARY = new String[]{"primary", "pri", "!"};
+    public static final String[] AUTOCREATED = new String[]{"autocreated", "aut", "a"};
+    public static final String[] MANDATORY = new String[]{"mandatory", "man", "m"};
+    public static final String[] PROTECTED = new String[]{"protected", "pro", "p"};
+    public static final String[] MULTIPLE = new String[]{"multiple", "mul", "*"};
+
+    public static final String[] COPY = new String[]{"copy", "Copy", "COPY"};
+    public static final String[] VERSION = new String[]{"version", "Version", "VERSION"};
+    public static final String[] INITIALIZE = new String[]{"initialize", "Initialize", "INITIALIZE"};
+    public static final String[] COMPUTE = new String[]{"compute", "Compute", "COMPUTE"};
+    public static final String[] IGNORE = new String[]{"ignore", "Ignore", "IGNORE"};
+    public static final String[] ABORT = new String[]{"abort", "Abort", "ABORT"};
+
+    public static final String[] ATTRIBUTE = new String[]{"primary", "pri", "!",
+                                                          "autocreated", "aut", "a",
+                                                          "mandatory", "man", "m",
+                                                          "protected", "pro", "p",
+                                                          "multiple", "mul", "*",
+                                                          "copy", "Copy", "COPY",
+                                                          "version", "Version", "VERSION",
+                                                          "initialize", "Initialize", "INITIALIZE",
+                                                          "compute", "Compute", "COMPUTE",
+                                                          "ignore", "Ignore", "IGNORE",
+                                                          "abort", "Abort", "ABORT"};
+
+    public static final String[] STRING = {"string", "String", "STRING"};
+    public static final String[] BINARY = {"binary", "Binary", "BINARY"};
+    public static final String[] LONG = {"long", "Long", "LONG"};
+    public static final String[] DOUBLE = {"double", "Double", "DOUBLE"};
+    public static final String[] BOOLEAN = {"boolean", "Boolean", "BOOLEAN"};
+    public static final String[] DATE = {"date", "Date", "DATE"};
+    public static final String[] NAME = {"name", "Name", "NAME"};
+    public static final String[] PATH = {"path", "Path", "PATH"};
+    public static final String[] REFERENCE = {"reference", "Reference", "REFERENCE"};
+
+    public static final String[] UNDEFINED = new String[]{"undefined", "Undefined", "UNDEFINED", "*"};
+
+    public static final String EOF = "eof";
+
+    private final StreamTokenizer st;
+
+    private final String systemId;
+
+    /**
+     * Constructor
+     * @param r
+     */
+    public Lexer(Reader r, String systemId) {
+        this.systemId = systemId;
+        st = new StreamTokenizer(r);
+
+        st.eolIsSignificant(false);
+
+        st.lowerCaseMode(false);
+
+        st.slashSlashComments(true);
+        st.slashStarComments(true);
+
+        st.wordChars('a', 'z');
+        st.wordChars('A', 'Z');
+        st.wordChars(':', ':');
+        st.wordChars('_', '_');
+
+        st.quoteChar(SINGLE_QUOTE);
+        st.quoteChar(DOUBLE_QUOTE);
+
+        st.ordinaryChar(BEGIN_NODE_TYPE_NAME);
+        st.ordinaryChar(END_NODE_TYPE_NAME);
+        st.ordinaryChar(EXTENDS);
+        st.ordinaryChar(LIST_DELIMITER);
+        st.ordinaryChar(PROPERTY_DEFINITION);
+        st.ordinaryChar(CHILD_NODE_DEFINITION);
+        st.ordinaryChar(BEGIN_TYPE);
+        st.ordinaryChar(END_TYPE);
+        st.ordinaryChar(DEFAULT);
+        st.ordinaryChar(CONSTRAINT);
+    }
+
+    /**
+     * getNextToken
+     *
+     * @return
+     * @throws ParseException
+     */
+    public String getNextToken() throws ParseException {
+        try {
+            int tokenType = st.nextToken();
+            if (tokenType == StreamTokenizer.TT_EOF) {
+                return EOF;
+            } else if (tokenType == StreamTokenizer.TT_WORD
+                    || tokenType == SINGLE_QUOTE
+                    || tokenType == DOUBLE_QUOTE) {
+                return st.sval;
+            } else if (tokenType == StreamTokenizer.TT_NUMBER) {
+                return String.valueOf(st.nval);
+            } else {
+                return new String(new char[] {(char) tokenType});
+            }
+        } catch (IOException e) {
+            fail("IOException while attempting to read input stream", e);
+            return null;
+        }
+    }
+
+    public void fail(String message) throws ParseException {
+        throw new ParseException(message, st.lineno(), -1, systemId);
+    }
+
+    public void fail(String message, Throwable e) throws ParseException {
+        throw new ParseException(message, e, st.lineno(), -1, systemId);
+    }
+
+    public void fail(Throwable e) throws ParseException {
+        throw new ParseException(e, st.lineno(), -1, systemId);
+    }
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\Lexer.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefReader.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefReader.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefReader.java	(revision 0)
@@ -0,0 +1,709 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+import javax.jcr.version.OnParentVersionAction;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceMapping;
+import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
+import org.apache.jackrabbit.spi.commons.nodetype.compact.QNodeTypeDefinitionsBuilder.QNodeDefinitionBuilder;
+import org.apache.jackrabbit.spi.commons.nodetype.compact.QNodeTypeDefinitionsBuilder.QNodeTypeDefinitionBuilder;
+import org.apache.jackrabbit.spi.commons.nodetype.compact.QNodeTypeDefinitionsBuilder.QPropertyDefinitionBuilder;
+import org.apache.jackrabbit.util.ISO9075;
+
+/**
+ * CompactNodeTypeDefReader. Parses node type definitions written in the compact
+ * node type definition format and returns a list of QNodeTypeDefinition objects that
+ * can then be used to register node types.
+ * <p/>
+ * The EBNF grammar of the compact node type definition:<br>
+ * <pre>
+ * cnd ::= ns_mapping* node_type_def+
+ *
+ * ns_mapping ::= "&lt;" prefix "=" namespace "&gt;"
+ *
+ * prefix ::= string
+ *
+ * namespace ::= string
+ *
+ * node_type_def ::= node_type_name [super_types] [options] {property_def | node_def}
+ *
+ * node_type_name ::= "[" string "]"
+ *
+ * super_types ::= "&gt;" string_list
+ *
+ * options ::= orderable_opt | mixin_opt | orderable_opt mixin_opt | mixin_opt orderable_opt
+ *
+ * orderable_opt ::= "orderable" | "ord" | "o"
+ *
+ * mixin_opt ::= "mixin" | "mix" | "m"
+ *
+ * property_def ::= "-" property_name [property_type_decl] [default_values] [attributes] [value_constraints]
+ *
+ * property_name ::= string
+ *
+ * property_type_decl ::= "(" property_type ")"
+ *
+ * property_type ::= "STRING" | "String |"string" |
+ *                   "BINARY" | "Binary" | "binary" |
+ *                   "LONG" | "Long" | "long" |
+ *                   "DOUBLE" | "Double" | "double" |
+ *                   "BOOLEAN" | "Boolean" | "boolean" |
+ *                   "DATE" | "Date" | "date" |
+ *                   "NAME | "Name | "name |
+ *                   "PATH" | "Path" | "path" |
+ *                   "REFERENCE" | "Reference" | "reference" |
+ *                   "UNDEFINED" | "Undefined" | "undefined" | "*"
+ *
+ *
+ * default_values ::= "=" string_list
+ *
+ * value_constraints ::= "&lt;" string_list
+ *
+ * node_def ::= "+" node_name [required_types] [default_type] [attributes]
+ *
+ * node_name ::= string
+ *
+ * required_types ::= "(" string_list ")"
+ *
+ * default_type ::= "=" string
+ *
+ * attributes ::= "primary" | "pri" | "!" |
+ *                "autocreated" | "aut" | "a" |
+ *                "mandatory" | "man" | "m" |
+ *                "protected" | "pro" | "p" |
+ *                "multiple" | "mul" | "*" |
+ *                "COPY" | "Copy" | "copy" |
+ *                "VERSION" | "Version" | "version" |
+ *                "INITIALIZE" | "Initialize" | "initialize" |
+ *                "COMPUTE" | "Compute" | "compute" |
+ *                "IGNORE" | "Ignore" | "ignore" |
+ *                "ABORT" | "Abort" | "abort"
+ *
+ * string_list ::= string {"," string}
+ *
+ * string ::= quoted_string | unquoted_string
+ *
+ * quoted_string :: = "'" unquoted_string "'"
+ *
+ * unquoted_string ::= [A-Za-z0-9:_]+
+ * </pre>
+ */
+public class CompactNodeTypeDefReader {
+
+    /**
+     * Empty array of value constraints
+     */
+    private final static String[] EMPTY_VALUE_CONSTRAINTS = new String[0];
+
+    /**
+     * the list of parsed QNodeTypeDefinition
+     */
+    private final List nodeTypeDefs = new LinkedList();
+
+    /**
+     * the current namespace mapping
+     */
+    private final NamespaceMapping nsMapping;
+
+    /**
+     * Name and Path resolver
+     */
+    private final NamePathResolver resolver;
+
+    /**
+     * the underlying lexer
+     */
+    private final Lexer lexer;
+
+    /**
+     * the current token
+     */
+    private String currentToken;
+
+    /**
+     * The builder for QNodeTypeDefinitions
+     */
+    private final QNodeTypeDefinitionsBuilder builder;
+
+    /**
+     * Creates a new CND reader.
+     * @param r
+     * @param systemId
+     * @param builder
+     * @throws ParseException
+     */
+    public CompactNodeTypeDefReader(Reader r, String systemId, QNodeTypeDefinitionsBuilder builder) throws ParseException {
+        this(r, systemId, new NamespaceMapping(), builder);
+    }
+
+
+    /**
+     * Creates a new CND reader.
+     * @param r
+     * @param builder
+     * @throws ParseException
+     */
+    public CompactNodeTypeDefReader(Reader r, String systemId, NamespaceMapping mapping,
+            QNodeTypeDefinitionsBuilder builder) throws ParseException {
+
+        this.builder = builder;
+        lexer = new Lexer(r, systemId);
+        this.nsMapping = mapping;
+        this.resolver = new DefaultNamePathResolver(nsMapping);
+        nextToken();
+        parse();
+    }
+
+    /**
+     * Returns the list of parsed QNodeTypeDefinition definitions.
+     *
+     * @return a List of QNodeTypeDefinition objects
+     */
+    public List getNodeTypeDefs() {
+        return nodeTypeDefs;
+    }
+
+    /**
+     * Returns the namespace mapping.
+     *
+     * @return a NamespaceMapping object.
+     */
+    public NamespaceMapping getNamespaceMapping() {
+        return nsMapping;
+    }
+
+    /**
+     * Parses the definition
+     *
+     * @throws ParseException
+     */
+    private void parse() throws ParseException {
+        while (!currentTokenEquals(Lexer.EOF)) {
+            if (!doNameSpace()) {
+                break;
+            }
+        }
+        while (!currentTokenEquals(Lexer.EOF)) {
+            QNodeTypeDefinitionBuilder ntd = builder.newQNodeTypeDefinition();
+            ntd.setOrderableChildNodes(false);
+            ntd.setMixin(false);
+            ntd.setPrimaryItemName(null);
+            doNodeTypeName(ntd);
+            doSuperTypes(ntd);
+            doOptions(ntd);
+            doItemDefs(ntd);
+            nodeTypeDefs.add(ntd.build());
+        }
+    }
+
+
+
+    /**
+     * processes the namespace declaration
+     *
+     * @return
+     * @throws ParseException
+     */
+    private boolean doNameSpace() throws ParseException {
+        if (!currentTokenEquals('<')) {
+            return false;
+        }
+        nextToken();
+        String prefix = currentToken;
+        nextToken();
+        if (!currentTokenEquals('=')) {
+            lexer.fail("Missing = in namespace decl.");
+        }
+        nextToken();
+        String uri = currentToken;
+        nextToken();
+        if (!currentTokenEquals('>')) {
+            lexer.fail("Missing > in namespace decl.");
+        }
+        try {
+            nsMapping.setMapping(prefix, uri);
+        } catch (NamespaceException e) {
+            // ignore
+        }
+        nextToken();
+        return true;
+    }
+
+    /**
+     * processes the nodetype name
+     *
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doNodeTypeName(QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        if (!currentTokenEquals(Lexer.BEGIN_NODE_TYPE_NAME)) {
+            lexer.fail("Missing '" + Lexer.BEGIN_NODE_TYPE_NAME + "' delimiter for beginning of node type name");
+        }
+        nextToken();
+        ntd.setName(toName(currentToken));
+
+        nextToken();
+        if (!currentTokenEquals(Lexer.END_NODE_TYPE_NAME)) {
+            lexer.fail("Missing '" + Lexer.END_NODE_TYPE_NAME + "' delimiter for end of node type name, found " + currentToken);
+        }
+        nextToken();
+    }
+
+    /**
+     * processes the superclasses
+     *
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doSuperTypes(QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        // a set would be nicer here, in case someone defines a supertype twice.
+        // but due to issue [JCR-333], the resulting node type definition is
+        // not symmetric anymore and the tests will fail.
+        ArrayList supertypes = new ArrayList();
+        if (currentTokenEquals(Lexer.EXTENDS))
+            do {
+                nextToken();
+                supertypes.add(toName(currentToken));
+                nextToken();
+            } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+
+        ntd.setSupertypes((Name[]) supertypes.toArray(new Name[0]));
+    }
+
+    /**
+     * processes the options
+     *
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doOptions(QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        if (currentTokenEquals(Lexer.ORDERABLE)) {
+            ntd.setOrderableChildNodes(true);
+            nextToken();
+            if (currentTokenEquals(Lexer.MIXIN)) {
+                ntd.setMixin(true);
+                nextToken();
+            }
+        } else if (currentTokenEquals(Lexer.MIXIN)) {
+            ntd.setMixin(true);
+            nextToken();
+            if (currentTokenEquals(Lexer.ORDERABLE)) {
+                ntd.setOrderableChildNodes(true);
+                nextToken();
+            }
+        }
+    }
+
+    /**
+     * processes the item definitions
+     *
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doItemDefs(QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        List propertyDefinitions = new ArrayList();
+        List nodeDefinitions = new ArrayList();
+        while (currentTokenEquals(Lexer.PROPERTY_DEFINITION) || currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) {
+            if (currentTokenEquals(Lexer.PROPERTY_DEFINITION)) {
+                QPropertyDefinitionBuilder pd = ntd.newQPropertyDefinition();
+
+                pd.setAutoCreated(false);
+                pd.setDeclaringNodeType(ntd.getName());
+                pd.setDefaultValues(null);
+                pd.setMandatory(false);
+                pd.setMultiple(false);
+                pd.setOnParentVersion(OnParentVersionAction.COPY);
+                pd.setProtected(false);
+                pd.setRequiredType(PropertyType.STRING);
+                pd.setValueConstraints(EMPTY_VALUE_CONSTRAINTS);
+
+                nextToken();
+                doPropertyDefinition(pd, ntd);
+                propertyDefinitions.add(pd.build());
+
+            } else if (currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) {
+                QNodeDefinitionBuilder nd = ntd.newQNodeDefinitionBuilder();
+
+                nd.setAllowsSameNameSiblings(false);
+                nd.setAutoCreated(false);
+                nd.setDeclaringNodeType(ntd.getName());
+                nd.setMandatory(false);
+                nd.setOnParentVersion(OnParentVersionAction.COPY);
+                nd.setProtected(false);
+                nd.setDefaultPrimaryType(null);
+                nd.setRequiredPrimaryTypes(new Name[]{NameConstants.NT_BASE});
+
+                nextToken();
+                doChildNodeDefinition(nd, ntd);
+                nodeDefinitions.add(nd.build());
+            }
+        }
+
+        ntd.setPropertyDefs((QPropertyDefinition[]) propertyDefinitions
+                .toArray(new QPropertyDefinition[0]));
+
+        ntd.setChildNodeDefs((QNodeDefinition[]) nodeDefinitions.toArray(new QNodeDefinition[0]));
+    }
+
+    /**
+     * processes the property definition
+     *
+     * @param pd
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doPropertyDefinition(QPropertyDefinitionBuilder pd, QNodeTypeDefinitionBuilder ntd)
+            throws ParseException {
+        if (currentToken.equals("*")) {
+            pd.setName(NameConstants.ANY_NAME);
+        } else {
+            pd.setName(toName(currentToken));
+        }
+        nextToken();
+        doPropertyType(pd);
+        doPropertyDefaultValue(pd);
+        doPropertyAttributes(pd, ntd);
+        doPropertyValueConstraints(pd);
+    }
+
+    /**
+     * processes the property type
+     *
+     * @param pd
+     * @throws ParseException
+     */
+    private void doPropertyType(QPropertyDefinitionBuilder pd) throws ParseException {
+        if (!currentTokenEquals(Lexer.BEGIN_TYPE)) {
+            return;
+        }
+        nextToken();
+        if (currentTokenEquals(Lexer.STRING)) {
+            pd.setRequiredType(PropertyType.STRING);
+        } else if (currentTokenEquals(Lexer.BINARY)) {
+            pd.setRequiredType(PropertyType.BINARY);
+        } else if (currentTokenEquals(Lexer.LONG)) {
+            pd.setRequiredType(PropertyType.LONG);
+        } else if (currentTokenEquals(Lexer.DOUBLE)) {
+            pd.setRequiredType(PropertyType.DOUBLE);
+        } else if (currentTokenEquals(Lexer.BOOLEAN)) {
+            pd.setRequiredType(PropertyType.BOOLEAN);
+        } else if (currentTokenEquals(Lexer.DATE)) {
+            pd.setRequiredType(PropertyType.DATE);
+        } else if (currentTokenEquals(Lexer.NAME)) {
+            pd.setRequiredType(PropertyType.NAME);
+        } else if (currentTokenEquals(Lexer.PATH)) {
+            pd.setRequiredType(PropertyType.PATH);
+        } else if (currentTokenEquals(Lexer.REFERENCE)) {
+            pd.setRequiredType(PropertyType.REFERENCE);
+        } else if (currentTokenEquals(Lexer.UNDEFINED)) {
+            pd.setRequiredType(PropertyType.UNDEFINED);
+        } else {
+            lexer.fail("Unkown property type '" + currentToken + "' specified");
+        }
+        nextToken();
+        if (!currentTokenEquals(Lexer.END_TYPE)) {
+            lexer.fail("Missing '" + Lexer.END_TYPE + "' delimiter for end of property type");
+        }
+        nextToken();
+    }
+
+    /**
+     * processes the property attributes
+     *
+     * @param pd
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doPropertyAttributes(QPropertyDefinitionBuilder pd, QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        while (currentTokenEquals(Lexer.ATTRIBUTE)) {
+            if (currentTokenEquals(Lexer.PRIMARY)) {
+                if (ntd.getPrimaryItemName() != null) {
+                    String name = null;
+                    try {
+                        name = resolver.getJCRName(ntd.getName());
+                    } catch (NamespaceException e) {
+                        // Should never happen, checked earlier
+                    }
+                    lexer.fail("More than one primary item specified in node type '" + name + "'");
+                }
+                ntd.setPrimaryItemName(pd.getName());
+            } else if (currentTokenEquals(Lexer.AUTOCREATED)) {
+                pd.setAutoCreated(true);
+            } else if (currentTokenEquals(Lexer.MANDATORY)) {
+                pd.setMandatory(true);
+            } else if (currentTokenEquals(Lexer.PROTECTED)) {
+                pd.setProtected(true);
+            } else if (currentTokenEquals(Lexer.MULTIPLE)) {
+                pd.setMultiple(true);
+            } else if (currentTokenEquals(Lexer.COPY)) {
+                pd.setOnParentVersion(OnParentVersionAction.COPY);
+            } else if (currentTokenEquals(Lexer.VERSION)) {
+                pd.setOnParentVersion(OnParentVersionAction.VERSION);
+            } else if (currentTokenEquals(Lexer.INITIALIZE)) {
+                pd.setOnParentVersion(OnParentVersionAction.INITIALIZE);
+            } else if (currentTokenEquals(Lexer.COMPUTE)) {
+                pd.setOnParentVersion(OnParentVersionAction.COMPUTE);
+            } else if (currentTokenEquals(Lexer.IGNORE)) {
+                pd.setOnParentVersion(OnParentVersionAction.IGNORE);
+            } else if (currentTokenEquals(Lexer.ABORT)) {
+                pd.setOnParentVersion(OnParentVersionAction.ABORT);
+            }
+            nextToken();
+        }
+    }
+
+    /**
+     * processes the property default values
+     *
+     * @param pd
+     * @throws ParseException
+     */
+    private void doPropertyDefaultValue(QPropertyDefinitionBuilder pd) throws ParseException {
+        if (!currentTokenEquals(Lexer.DEFAULT)) {
+            return;
+        }
+        List defaultValues = new ArrayList();
+        do {
+            nextToken();
+            QValue value = null;
+            try {
+                value = pd.createValue(currentToken, resolver);
+            } catch (ValueFormatException e) {
+                lexer.fail("'" + currentToken + "' is not a valid string representation of a value of type " + pd.getRequiredType());
+            } catch (RepositoryException e) {
+                lexer.fail("An error occured during value conversion of '" + currentToken + "'");
+            }
+            defaultValues.add(value);
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        pd.setDefaultValues((QValue[]) defaultValues.toArray(new QValue[0]));
+    }
+
+    /**
+     * processes the property value constraints
+     *
+     * @param pd
+     * @throws ParseException
+     */
+    private void doPropertyValueConstraints(QPropertyDefinitionBuilder pd) throws ParseException {
+        if (!currentTokenEquals(Lexer.CONSTRAINT)) {
+            return;
+        }
+        List constraints = new ArrayList();
+        do {
+            nextToken();
+            String constraint = null;
+            try {
+                constraint = pd.createValueConstraint(currentToken, resolver);
+            } catch (InvalidConstraintException e) {
+                lexer.fail("'" + currentToken + "' is not a valid constraint expression for a value of type " + pd.getRequiredType());
+            }
+            constraints.add(constraint);
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        pd.setValueConstraints((String[]) constraints.toArray(new String[0]));
+    }
+
+    /**
+     * processes the childnode definition
+     *
+     * @param nd
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doChildNodeDefinition(QNodeDefinitionBuilder nd, QNodeTypeDefinitionBuilder ntd)
+            throws ParseException {
+        if (currentTokenEquals('*')) {
+            nd.setName(NameConstants.ANY_NAME);
+        } else {
+            nd.setName(toName(currentToken));
+        }
+        nextToken();
+        doChildNodeRequiredTypes(nd);
+        doChildNodeDefaultType(nd);
+        doChildNodeAttributes(nd, ntd);
+    }
+
+    /**
+     * processes the childnode required types
+     *
+     * @param nd
+     * @throws ParseException
+     */
+    private void doChildNodeRequiredTypes(QNodeDefinitionBuilder nd) throws ParseException {
+        if (!currentTokenEquals(Lexer.BEGIN_TYPE)) {
+            return;
+        }
+        List types = new ArrayList();
+        do {
+            nextToken();
+            types.add(toName(currentToken));
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        nd.setRequiredPrimaryTypes((Name[]) types.toArray(new Name[0]));
+        nextToken();
+    }
+
+    /**
+     * processes the childnode default types
+     *
+     * @param nd
+     * @throws ParseException
+     */
+    private void doChildNodeDefaultType(QNodeDefinitionBuilder nd) throws ParseException {
+        if (!currentTokenEquals(Lexer.DEFAULT)) {
+            return;
+        }
+        nextToken();
+        nd.setDefaultPrimaryType(toName(currentToken));
+        nextToken();
+    }
+
+    /**
+     * processes the childnode attributes
+     *
+     * @param nd
+     * @param ntd
+     * @throws ParseException
+     */
+    private void doChildNodeAttributes(QNodeDefinitionBuilder nd, QNodeTypeDefinitionBuilder ntd) throws ParseException {
+        while (currentTokenEquals(Lexer.ATTRIBUTE)) {
+            if (currentTokenEquals(Lexer.PRIMARY)) {
+                if (ntd.getPrimaryItemName() != null) {
+                    String name = null;
+                    try {
+                        name = resolver.getJCRName(ntd.getName());
+                    } catch (NamespaceException e) {
+                        // Should never happen, checked earlier
+                    }
+                    lexer.fail("More than one primary item specified in node type '" + name + "'");
+                }
+                ntd.setPrimaryItemName(nd.getName());
+            } else if (currentTokenEquals(Lexer.AUTOCREATED)) {
+                nd.setAutoCreated(true);
+            } else if (currentTokenEquals(Lexer.MANDATORY)) {
+                nd.setMandatory(true);
+            } else if (currentTokenEquals(Lexer.PROTECTED)) {
+                nd.setProtected(true);
+            } else if (currentTokenEquals(Lexer.MULTIPLE)) {
+                nd.setAllowsSameNameSiblings(true);
+            } else if (currentTokenEquals(Lexer.COPY)) {
+                nd.setOnParentVersion(OnParentVersionAction.COPY);
+            } else if (currentTokenEquals(Lexer.VERSION)) {
+                nd.setOnParentVersion(OnParentVersionAction.VERSION);
+            } else if (currentTokenEquals(Lexer.INITIALIZE)) {
+                nd.setOnParentVersion(OnParentVersionAction.INITIALIZE);
+            } else if (currentTokenEquals(Lexer.COMPUTE)) {
+                nd.setOnParentVersion(OnParentVersionAction.COMPUTE);
+            } else if (currentTokenEquals(Lexer.IGNORE)) {
+                nd.setOnParentVersion(OnParentVersionAction.IGNORE);
+            } else if (currentTokenEquals(Lexer.ABORT)) {
+                nd.setOnParentVersion(OnParentVersionAction.ABORT);
+            }
+            nextToken();
+        }
+    }
+
+    /**
+     * Converts the given string into a qualified name using the current
+     * namespace mapping.
+     *
+     * @param stringName
+     * @return the qualified name
+     * @throws ParseException if the conversion fails
+     */
+    private Name toName(String stringName) throws ParseException {
+        try {
+            Name n = resolver.getQName(stringName);
+            String decodedLocalName = ISO9075.decode(n.getLocalName());
+            return builder.createName(n.getNamespaceURI(), decodedLocalName);
+        } catch (NameException e) {
+            lexer.fail("Error while parsing '" + stringName + "'", e);
+            return null;
+        } catch (NamespaceException e) {
+            lexer.fail("Error while parsing '" + stringName + "'", e);
+            return null;
+        }
+    }
+
+    /**
+     * Gets the next token from the underlying lexer.
+     *
+     * @see Lexer#getNextToken()
+     * @throws ParseException if the lexer fails to get the next token.
+     */
+    private void nextToken() throws ParseException {
+        currentToken = lexer.getNextToken();
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param s the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(String[] s) {
+        for (int i = 0; i < s.length; i++) {
+            if (currentToken.equals(s[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param c the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(char c) {
+        return currentToken.length() == 1 && currentToken.charAt(0) == c;
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param s the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(String s) {
+        return currentToken.equals(s);
+    }
+
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\CompactNodeTypeDefReader.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/ParseException.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/ParseException.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/ParseException.java	(revision 0)
@@ -0,0 +1,128 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+/**
+ * ParseException
+ */
+public class ParseException extends Exception {
+
+    /**
+     * the line number where the error occurred
+     */
+    private final int lineNumber;
+
+    /**
+     * the column number where the error occurred
+     */
+    private final int colNumber;
+
+    /**
+     * the systemid of the source that produced the error
+     */
+    private final String systemId;
+
+
+    /**
+     * Constructs a new instance of this class with <code>null</code> as its
+     * detail message.
+     */
+    public ParseException(int lineNumber, int colNumber, String systemId) {
+        super();
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public ParseException(String message, int lineNumber, int colNumber, String systemId) {
+        super(message);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message   the detail message. The detail message is saved for
+     *                  later retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public ParseException(String message, Throwable rootCause, int lineNumber, int colNumber, String systemId) {
+        super(message, rootCause);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified root cause.
+     *
+     * @param rootCause root failure cause
+     */
+    public ParseException(Throwable rootCause, int lineNumber, int colNumber, String systemId) {
+        super(rootCause);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMessage() {
+        StringBuffer b = new StringBuffer(super.getMessage());
+        String delim = " (";
+        if (systemId != null && !systemId.equals("")) {
+            b.append(delim);
+            b.append(systemId);
+            delim = ", ";
+        }
+        if (lineNumber >= 0) {
+            b.append(delim);
+            b.append("line ");
+            b.append(lineNumber);
+            delim = ", ";
+        }
+        if (colNumber >= 0) {
+            b.append(delim);
+            b.append("col ");
+            b.append(colNumber);
+            delim = ", ";
+        }
+        if (delim.equals(", ")) {
+            b.append(")");
+        }
+        return b.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return super.toString(); // + " (" + systemId + ", line " + lineNumber +", col " + colNumber +")";
+    }
+
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\ParseException.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefWriter.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefWriter.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefWriter.java	(revision 0)
@@ -0,0 +1,470 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.version.OnParentVersionAction;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl;
+import org.apache.jackrabbit.spi.commons.value.ValueFormat;
+import org.apache.jackrabbit.util.ISO9075;
+
+/**
+ * Prints node type defs in a compact notation
+ * Print Format:
+ * <ex = "http://apache.org/jackrabbit/example">
+ * [ex:NodeType] > ex:ParentType1, ex:ParentType2
+ * orderable mixin
+ *   - ex:property (STRING) = 'default1', 'default2'
+ *     primary mandatory autocreated protected multiple VERSION
+ *     < 'constraint1', 'constraint2'
+ *   + ex:node (ex:reqType1, ex:reqType2) = ex:defaultType
+ *     mandatory autocreated protected multiple VERSION
+ */
+public class CompactNodeTypeDefWriter {
+
+    /**
+     * the indention string
+     */
+    private static final String INDENT = "  ";
+
+    /**
+     * the current namespace resolver
+     */
+    private final NamespaceResolver resolver;
+
+    /**
+     * the current name/path resolver
+     */
+    private final NamePathResolver npResolver;
+
+    /**
+     * the current value factory
+     */
+    private final ValueFactory valueFactory;
+
+    /**
+     * the underlying writer
+     */
+    private Writer out;
+
+    /**
+     * special writer used for namespaces
+     */
+    private Writer nsWriter;
+
+    /**
+     * namespaces(prefixes) that are used
+     */
+    private final HashSet usedNamespaces = new HashSet();
+
+    /**
+     * Creates a new nodetype writer
+     *
+     * @param out the underlying writer
+     * @param r the namespace resolver
+     * @param npResolver
+     * @param valueFactory
+     */
+    public CompactNodeTypeDefWriter(Writer out, NamespaceResolver r, NamePathResolver npResolver,
+            ValueFactory valueFactory) {
+        this(out, r, npResolver, valueFactory, false);
+    }
+
+    /**
+     * Creates a new nodetype writer
+     *
+     * @param out the underlaying writer
+     * @param r the naespace resolver
+     * @param npResolver
+     * @param valueFactory
+     * @param includeNS if <code>true</code> all used namespace decl. are also
+     */
+    public CompactNodeTypeDefWriter(Writer out, NamespaceResolver r, NamePathResolver npResolver,
+            ValueFactory valueFactory, boolean includeNS) {
+        this.resolver = r;
+        this.npResolver = npResolver;
+        this.valueFactory = valueFactory;
+        if (includeNS) {
+            this.out = new StringWriter();
+            this.nsWriter = out;
+        } else {
+            this.out = out;
+            this.nsWriter = null;
+        }
+    }
+
+    /**
+     * Writes the given list of QNodeTypeDefinition to the output writer including the
+     * used namespaces.
+     *
+     * @param l
+     * @param r
+     * @param npResolver
+     * @param valueFactory
+     * @param out
+     * @throws IOException
+     */
+    public static void write(List l, NamespaceResolver r, NamePathResolver npResolver,
+            ValueFactory valueFactory, Writer out)
+            throws IOException {
+        CompactNodeTypeDefWriter w = new CompactNodeTypeDefWriter(out, r, npResolver, valueFactory, true);
+        Iterator iter = l.iterator();
+        while (iter.hasNext()) {
+            QNodeTypeDefinition def = (QNodeTypeDefinition) iter.next();
+            w.write(def);
+        }
+        w.close();
+    }
+
+    /**
+     * Write one QNodeTypeDefinition to this writer
+     *
+     * @param ntd
+     * @throws IOException
+     */
+    public void write(QNodeTypeDefinition ntd) throws IOException {
+        writeName(ntd);
+        writeSupertypes(ntd);
+        writeOptions(ntd);
+        writePropDefs(ntd);
+        writeNodeDefs(ntd);
+        out.write("\n\n");
+    }
+
+    /**
+     * Flushes all pending write operations and Closes this writer. please note,
+     * that the underlying writer remains open.
+     *
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        if (nsWriter != null) {
+            nsWriter.write("\n");
+            out.close();
+            nsWriter.write(((StringWriter) out).getBuffer().toString());
+            out = nsWriter;
+            nsWriter = null;
+        }
+        out.flush();
+        out = null;
+    }
+
+    /**
+     * write name
+     */
+    private void writeName(QNodeTypeDefinition ntd) throws IOException {
+        out.write("[");
+        out.write(resolve(ntd.getName()));
+        out.write("]");
+    }
+
+    /**
+     * write supertypes
+     */
+    private void writeSupertypes(QNodeTypeDefinition ntd) throws IOException {
+        Name[] sta = ntd.getSupertypes();
+        String delim = " > ";
+        for (int i = 0; i < sta.length; i++) {
+            out.write(delim);
+            out.write(resolve(sta[i]));
+            delim = ", ";
+        }
+    }
+
+    /**
+     * write options
+     */
+    private void writeOptions(QNodeTypeDefinition ntd) throws IOException {
+        if (ntd.hasOrderableChildNodes()) {
+            out.write("\n" + INDENT);
+            out.write("orderable");
+            if (ntd.isMixin()) {
+                out.write(" mixin");
+            }
+        } else if (ntd.isMixin()) {
+            out.write("\n" + INDENT);
+            out.write("mixin");
+        }
+    }
+
+    /**
+     * write prop defs
+     */
+    private void writePropDefs(QNodeTypeDefinition ntd) throws IOException {
+        QPropertyDefinition[] pda = ntd.getPropertyDefs();
+        for (int i = 0; i < pda.length; i++) {
+            QPropertyDefinition pd = pda[i];
+            writePropDef(ntd, pd);
+        }
+    }
+
+    /**
+     * write node defs
+     */
+    private void writeNodeDefs(QNodeTypeDefinition ntd) throws IOException {
+        QNodeDefinition[] nda = ntd.getChildNodeDefs();
+        for (int i = 0; i < nda.length; i++) {
+            QNodeDefinition nd = nda[i];
+            writeNodeDef(ntd, nd);
+        }
+    }
+
+    /**
+     * write prop def
+     * @param pd
+     */
+    private void writePropDef(QNodeTypeDefinition ntd, QPropertyDefinition pd) throws IOException {
+        out.write("\n" + INDENT + "- ");
+
+        Name name = pd.getName();
+        if (name.equals(NameConstants.ANY_NAME)) {
+            out.write('*');
+        } else {
+            writeItemDefName(name);
+        }
+
+        out.write(" (");
+        out.write(PropertyType.nameFromValue(pd.getRequiredType()).toLowerCase());
+        out.write(")");
+        writeDefaultValues(pd.getDefaultValues());
+        out.write(ntd.getPrimaryItemName() != null && ntd.getPrimaryItemName().equals(pd.getName()) ? " primary" : "");
+        if (pd.isMandatory()) {
+            out.write(" mandatory");
+        }
+        if (pd.isAutoCreated()) {
+            out.write(" autocreated");
+        }
+        if (pd.isProtected()) {
+            out.write(" protected");
+        }
+        if (pd.isMultiple()) {
+            out.write(" multiple");
+        }
+        if (pd.getOnParentVersion() != OnParentVersionAction.COPY) {
+            out.write(" ");
+            out.write(OnParentVersionAction.nameFromValue(pd.getOnParentVersion()).toLowerCase());
+        }
+        writeValueConstraints(pd.getValueConstraints(), pd.getRequiredType());
+    }
+
+    /**
+     * write default values
+     * @param dva
+     */
+    private void writeDefaultValues(QValue[] dva) throws IOException {
+        if (dva != null && dva.length > 0) {
+            String delim = " = '";
+            for (int i = 0; i < dva.length; i++) {
+                out.write(delim);
+                try {
+                    Value v = ValueFormat.getJCRValue(dva[i], npResolver, valueFactory);
+                    out.write(escape(v.getString()));
+                } catch (RepositoryException e) {
+                    out.write(escape(dva[i].toString()));
+                }
+                out.write("'");
+                delim = ", '";
+            }
+        }
+    }
+
+    /**
+     * write value constraints
+     * @param vca
+     */
+    private void writeValueConstraints(String[] vca, int type) throws IOException {
+        if (vca != null && vca.length > 0) {
+            String vc = convertConstraint(vca[0], type);
+            out.write(" < '");
+            out.write(escape(vc));
+            out.write("'");
+            for (int i = 1; i < vca.length; i++) {
+                vc = convertConstraint(vca[i], type);
+                out.write(", '");
+                out.write(escape(vc));
+                out.write("'");
+            }
+        }
+    }
+
+    private String convertConstraint(String vc, int type) {
+        if (type == PropertyType.REFERENCE || type == PropertyType.NAME || type == PropertyType.PATH) {
+            if (type == PropertyType.REFERENCE)
+                type = PropertyType.NAME;
+
+            try {
+                QValue qv = QValueFactoryImpl.getInstance().create(vc, type);
+                vc = ValueFormat.getJCRValue(qv, npResolver, valueFactory).getString();
+            }
+            catch (RepositoryException e) {
+                // ignore -> return unconverted constraint
+            }
+        }
+
+        return vc;
+    }
+
+    /**
+     * write node def
+     *
+     * @param nd
+     */
+    private void writeNodeDef(QNodeTypeDefinition ntd, QNodeDefinition nd) throws IOException {
+        out.write("\n" + INDENT + "+ ");
+
+        Name name = nd.getName();
+        if (name.equals(NameConstants.ANY_NAME)) {
+            out.write('*');
+        } else {
+            writeItemDefName(name);
+        }
+        writeRequiredTypes(nd.getRequiredPrimaryTypes());
+        writeDefaultType(nd.getDefaultPrimaryType());
+        out.write(ntd.getPrimaryItemName() != null && ntd.getPrimaryItemName().equals(nd.getName()) ? " primary" : "");
+        if (nd.isMandatory()) {
+            out.write(" mandatory");
+        }
+        if (nd.isAutoCreated()) {
+            out.write(" autocreated");
+        }
+        if (nd.isProtected()) {
+            out.write(" protected");
+        }
+        if (nd.allowsSameNameSiblings()) {
+            out.write(" multiple");
+        }
+        if (nd.getOnParentVersion() != OnParentVersionAction.COPY) {
+            out.write(" ");
+            out.write(OnParentVersionAction.nameFromValue(nd.getOnParentVersion()).toLowerCase());
+        }
+    }
+
+    /**
+     * Write item def name
+     * @param name
+     * @throws IOException
+     */
+    private void writeItemDefName(Name name) throws IOException {
+        out.write(resolve(name));
+    }
+    /**
+     * write required types
+     * @param reqTypes
+     */
+    private void writeRequiredTypes(Name[] reqTypes) throws IOException {
+        if (reqTypes != null && reqTypes.length > 0) {
+            String delim = " (";
+            for (int i = 0; i < reqTypes.length; i++) {
+                out.write(delim);
+                out.write(resolve(reqTypes[i]));
+                delim = ", ";
+            }
+            out.write(")");
+        }
+    }
+
+    /**
+     * write default types
+     * @param defType
+     */
+    private void writeDefaultType(Name defType) throws IOException {
+        if (defType != null && !defType.getLocalName().equals("*")) {
+            out.write(" = ");
+            out.write(resolve(defType));
+        }
+    }
+
+    /**
+     * resolve
+     * @param name
+     * @return the resolved name
+     */
+    private String resolve(Name name) throws IOException {
+        if (name == null) {
+            return "";
+        }
+        try {
+            String prefix = resolver.getPrefix(name.getNamespaceURI());
+            if (prefix != null && !prefix.equals(Name.NS_EMPTY_PREFIX)) {
+                // check for writing namespaces
+                if (nsWriter != null) {
+                    if (!usedNamespaces.contains(prefix)) {
+                        usedNamespaces.add(prefix);
+                        nsWriter.write("<'");
+                        nsWriter.write(prefix);
+                        nsWriter.write("'='");
+                        nsWriter.write(escape(name.getNamespaceURI()));
+                        nsWriter.write("'>\n");
+                    }
+                }
+                prefix += ":";
+            }
+
+            String encLocalName = ISO9075.encode(name.getLocalName());
+            String resolvedName = prefix + encLocalName;
+
+            // check for '-' and '+'
+            if (resolvedName.indexOf('-') >= 0 || resolvedName.indexOf('+') >= 0) {
+                return "'" + resolvedName + "'";
+            } else {
+                return resolvedName;
+            }
+
+        } catch (NamespaceException e) {
+            return name.toString();
+        }
+    }
+
+    /**
+     * escape
+     * @param s
+     * @return the escaped string
+     */
+    private String escape(String s) {
+        StringBuffer sb = new StringBuffer(s);
+        for (int i = 0; i < sb.length(); i++) {
+            if (sb.charAt(i) == '\\') {
+                sb.insert(i, '\\');
+                i++;
+            } else if (sb.charAt(i) == '\'') {
+                sb.insert(i, '\'');
+                i++;
+            }
+        }
+        return sb.toString();
+    }
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\CompactNodeTypeDefWriter.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilder.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilder.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilder.java	(revision 0)
@@ -0,0 +1,453 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QItemDefinition;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
+
+/**
+ * A builder for {@link QNodeTypeDefinition}s
+ */
+public abstract class QNodeTypeDefinitionsBuilder {
+
+    /**
+     * @return a new instance of a builder for a {@link QNodeTypeDefinition}
+     */
+    public abstract QNodeTypeDefinitionBuilder newQNodeTypeDefinition();
+
+    /**
+     * Returns a <code>Name</code> with the given namespace URI and
+     * local part and validates the given parameters.
+     *
+     * @param namespaceURI namespace uri
+     * @param localName local part
+     * @throws IllegalArgumentException if <code>namespaceURI</code> or
+     * <code>localName</code> is invalid.
+     */
+    public abstract Name createName(String namespaceURI, String localName) throws IllegalArgumentException;
+
+    /**
+     * A builder for a {@link QNodeTypeDefinition}
+     */
+    public abstract class QNodeTypeDefinitionBuilder {
+        private Name name;
+        private Name[] supertypes;
+        private boolean isMixin;
+        private boolean isOrderable;
+        private Name primaryItemName;
+        private QPropertyDefinition[] propertyDefinitions;
+        private QNodeDefinition[] childNodeDefinitions;
+
+        /**
+         * Set the name of the node type definition being built
+         * @param name
+         */
+        public void setName(Name name) {
+            this.name = name;
+        }
+
+        /**
+         * @return the name of the node type definition being built or <code>null</code> if not set.
+         */
+        public Name getName() {
+            return name;
+        }
+
+        /**
+         * Specifies the supertypes of the node type definition being built
+         * @param supertypes
+         */
+        public void setSupertypes(Name[] supertypes) {
+            this.supertypes = supertypes;
+        }
+
+        /**
+         * Returns an array containing the names of the supertypes of the node type definition being
+         * built.
+         *
+         * @return an array of supertype names
+         */
+        public Name[] getSuperTypes() {
+            return supertypes;
+        }
+
+        /**
+         * @param isMixin true if building a mixin node type definition; false otherwise.
+         */
+        public void setMixin(boolean isMixin) {
+            this.isMixin = isMixin;
+        }
+
+        /**
+         * @return true if building a mixin node type definition; false otherwise.
+         */
+        public boolean getMixin() {
+            return isMixin;
+        }
+
+        /**
+         * @param isOrderable true if building a node type having orderable child nodes; false
+         *                otherwise.
+         */
+        public void setOrderableChildNodes(boolean isOrderable) {
+            this.isOrderable = isOrderable;
+        }
+
+        /**
+         * @return true if building a node type having orderable child nodes; false otherwise.
+         */
+        public boolean getOrderableChildNodes() {
+            return isOrderable;
+        }
+
+        /**
+         * @param primaryItemName  the name of the primary item or <code>null</code> if not set.
+         */
+        public void setPrimaryItemName(Name primaryItemName) {
+            this.primaryItemName = primaryItemName;
+        }
+
+        /**
+         * @return the name of the primary item or <code>null</code> if not set.
+         */
+        public Name getPrimaryItemName() {
+            return primaryItemName;
+        }
+
+        /**
+         * @param propDefs an array containing the property definitions of the node type definition
+         *                being built.
+         */
+        public void setPropertyDefs(QPropertyDefinition[] propDefs) {
+            propertyDefinitions = propDefs;
+        }
+
+        /**
+         * @return an array containing the property definitions of the node type definition being
+         *         built or <code>null</code> if not set.
+         */
+        public QPropertyDefinition[] getPropertyDefs() {
+            return propertyDefinitions;
+        }
+
+        /**
+         * @param childDefs an array containing the child node definitions of the node type
+         *                definition being.
+         */
+        public void setChildNodeDefs(QNodeDefinition[] childDefs) {
+            childNodeDefinitions = childDefs;
+        }
+
+        /**
+         * @return an array containing the child node definitions of the node type definition being
+         *         built or <code>null</code> if not set.
+         */
+        public QNodeDefinition[] getChildNodeDefs() {
+            return childNodeDefinitions;
+        }
+
+        /**
+         * @return  a new instance of a builder for a {@link QNodeDefinition}.
+         */
+        public abstract QPropertyDefinitionBuilder newQPropertyDefinition();
+
+        /**
+         * @return  a new instance of a builder for a {@link QNodeDefinition}.
+         */
+        public abstract QNodeDefinitionBuilder newQNodeDefinitionBuilder();
+
+        /**
+         * Creates a new {@link QNodeTypeDefinition} instance based on the state of this builder.
+         *
+         * @return a new {@link QNodeTypeDefinition} instance.
+         * @throws IllegalStateException if the instance has not the necessary information to build
+         *                 the QNodeTypeDefinition instance.
+         */
+        public abstract QNodeTypeDefinition build() throws IllegalStateException;
+    }
+
+    /**
+     * A builder for a {@link QItemDefinition}
+     */
+    abstract class QItemDefinitionBuilder {
+        private Name name;
+        private Name declaringType;
+        private boolean isAutocreated;
+        private int onParentVersion;
+        private boolean isProtected;
+        private boolean isMandatory;
+
+        /**
+         * @param name  the name of the child item definition being build
+         */
+        public void setName(Name name) {
+            this.name = name;
+        }
+
+        /**
+         * @return the name of the child item definition being build.
+         */
+        public Name getName() {
+            return name;
+        }
+
+        /**
+         * @param type  the name of the declaring node type.
+         */
+        public void setDeclaringNodeType(Name type) {
+            declaringType = type;
+        }
+
+        /**
+         * @return the name of the declaring node type.
+         */
+        public Name getDeclaringNodeType() {
+            return declaringType;
+        }
+
+        /**
+         * @param autocreate  true if building a 'autocreate' child item definition, false otherwise.
+         */
+        public void setAutoCreated(boolean autocreate) {
+            isAutocreated = autocreate;
+        }
+
+        /**
+         * @return true if building a 'autocreate' child item definition, false otherwise.
+         */
+        public boolean getAutoCreated() {
+            return isAutocreated;
+        }
+
+        /**
+         * @param onParent the 'onParentVersion' attribute of the child item definition being built
+         */
+        public void setOnParentVersion(int onParent) {
+            onParentVersion = onParent;
+        }
+
+        /**
+         * @return the 'onParentVersion' attribute of the child item definition being built
+         */
+        public int getOnParentVersion() {
+            return onParentVersion;
+        }
+
+        /**
+         * @param isProtected true if building a 'protected' child item definition, false otherwise.
+         */
+        public void setProtected(boolean isProtected) {
+            this.isProtected = isProtected;
+        }
+
+        /**
+         * @return  true if building a 'protected' child item definition, false otherwise.
+         */
+        public boolean getProtected() {
+            return isProtected;
+        }
+
+        /**
+         * @param isMandatory true if building a 'mandatory' child item definition, false otherwise.
+         */
+        public void setMandatory(boolean isMandatory) {
+            this.isMandatory = isMandatory;
+        }
+
+        /**
+         * @return  true if building a 'mandatory' child item definition, false otherwise.
+         */
+        public boolean getMandatory() {
+            return isMandatory;
+        }
+    }
+
+    /**
+     * A builder for a {@link QNodeDefinition}
+     */
+    public abstract class QPropertyDefinitionBuilder extends QItemDefinitionBuilder {
+        private int requiredType;
+        private String[] valueConstraints;
+        private QValue[] defaultValues;
+        private boolean isMultiple;
+
+        /**
+         * @param type the required type of the property definition being built.
+         */
+        public void setRequiredType(int type) {
+            requiredType = type;
+        }
+
+        /**
+         * @return the required type of the property definition being built.
+         */
+        public int getRequiredType() {
+            return requiredType;
+        }
+
+        /**
+         * @param constraints array of value constraints of the property definition being built.
+         */
+        public void setValueConstraints(String[] constraints) {
+            valueConstraints = constraints;
+        }
+
+        /**
+         * @return array of value constraints of the property definition being built.
+         */
+        public String[] getValueConstraints() {
+            return valueConstraints;
+        }
+
+        /**
+         * @param values array of default values of the property definition being built.
+         */
+        public void setDefaultValues(QValue[] values) {
+            defaultValues = values;
+        }
+
+        /**
+         * @return array of default values of the property definition being built or
+         *         <code>null</code> if no default values are defined.
+         */
+        public QValue[] getDefaultValues() {
+            return defaultValues;
+        }
+
+        /**
+         * @param isMultiple true if building a 'multiple' property definition.
+         */
+        public void setMultiple(boolean isMultiple) {
+            this.isMultiple = isMultiple;
+        }
+
+        /**
+         * @return true if building a 'multiple' property definition.
+         */
+        public boolean getMultiple() {
+            return isMultiple;
+        }
+
+        /**
+         * Validate the given <code>constraint</code> and resolve any prefixes.
+         *
+         * @param constraint
+         * @param resolver
+         * @return A syntactically valid value constrained which refers to fully qualified names and
+         *         paths only.
+         * @throws InvalidConstraintException if <code>constraint</code> cannot be converted to a
+         *                 valid value constrained.
+         */
+        public abstract String createValueConstraint(String constraint, NamePathResolver resolver)
+                throws InvalidConstraintException;
+
+        /**
+         * Create a new <code>QValue</code> for <code>value</code> of the type this instance
+         * represents using the given <code>resolver</code>.
+         *
+         * @param value
+         * @param resolver
+         * @return a new <code>QValue</code>.
+         * @throws ValueFormatException If the given <code>value</code> cannot be converted to the
+         *                 specified <code>type</code>.
+         * @throws RepositoryException If another error occurs.
+         */
+        public abstract QValue createValue(String value, NamePathResolver resolver)
+                throws ValueFormatException, RepositoryException;
+
+        /**
+         * Creates a new {@link QPropertyDefinition} instance based on the state of this builder.
+         *
+         * @return a new {@link QPropertyDefinition} instance.
+         * @throws IllegalStateException if the instance has not the necessary information to build
+         *                 the QPropertyDefinition instance.
+         */
+        public abstract QPropertyDefinition build() throws IllegalStateException;
+    }
+
+    /**
+     * A builder for a {@link QNodeDefinition}
+     */
+    public abstract class QNodeDefinitionBuilder extends QItemDefinitionBuilder {
+        private Name defaultPrimaryType;
+        private Name[] requiredPrimaryTypes;
+        private boolean allowsSameNameSiblings;
+
+        /**
+         * @param name the name of the default primary type of the node definition being built.
+         */
+        public void setDefaultPrimaryType(Name name) {
+            defaultPrimaryType = name;
+        }
+
+        /**
+         * @return the name of the default primary type of the node definition being built.
+         */
+        public Name getDefaultPrimaryType() {
+            return defaultPrimaryType;
+        }
+
+        /**
+         * @param names array of names of the required primary types of the node definition being
+         *                built.
+         */
+        public void setRequiredPrimaryTypes(Name[] names) {
+            requiredPrimaryTypes = names;
+        }
+
+        /**
+         * @return array of names of the required primary types of the node definition being built.
+         */
+        public Name[] getRequiredPrimaryTypes() {
+            return requiredPrimaryTypes;
+        }
+
+        /**
+         * @param allowSns true if building a node definition with same name siblings, false
+         *                otherwise.
+         */
+        public void setAllowsSameNameSiblings(boolean allowSns) {
+            allowsSameNameSiblings = allowSns;
+        }
+
+        /**
+         * @return true if building a node definition with same name siblings, false otherwise.
+         */
+        public boolean getAllowsSameNameSiblings() {
+            return allowsSameNameSiblings;
+        }
+
+        /**
+         * Creates a new {@link QNodeDefinition} instance based on the state of this builder.
+         *
+         * @return a new {@link QNodeDefinition} instance.
+         * @throws IllegalStateException if the instance has not the necessary information to build
+         *                 the QNodeDefinition instance.
+         */
+        public abstract QNodeDefinition build() throws IllegalStateException;
+    }
+
+}

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\QNodeTypeDefinitionsBuilder.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilderImpl.java
===================================================================
--- jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilderImpl.java	(revision 0)
+++ jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilderImpl.java	(revision 0)
@@ -0,0 +1,138 @@
+/*
+ * 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.spi.commons.nodetype.compact;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.QNodeDefinitionImpl;
+import org.apache.jackrabbit.spi.commons.QNodeTypeDefinitionImpl;
+import org.apache.jackrabbit.spi.commons.QPropertyDefinitionImpl;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
+import org.apache.jackrabbit.spi.commons.value.QValueFactoryImpl;
+import org.apache.jackrabbit.spi.commons.value.ValueFormat;
+
+/**
+ * Default implementations of a {@link QNodeTypeDefinitionsBuilder}. This implementations uses
+ * {@link QNodeTypeDefinitionBuilderImpl} for building node type definitions,
+ * {@link QPropertyDefinitionBuilderImpl} for building property definitions, and
+ * {@link QNodeDefinitionBuilderImpl} for building node definitions. It further uses
+ * {@link NameFactoryImpl} for creating <code>Name</code>s and {@link QValueFactoryImpl} for
+ * creating <code>QValue</code>s.
+ */
+public class QNodeTypeDefinitionsBuilderImpl extends QNodeTypeDefinitionsBuilder {
+
+    private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+
+    public QNodeTypeDefinitionBuilder newQNodeTypeDefinition() {
+        return new QNodeTypeDefinitionBuilderImpl();
+    }
+
+    public Name createName(String namespaceURI, String localName) {
+        return NAME_FACTORY.create(namespaceURI, localName);
+    }
+
+    /**
+     * Default implementation of a {@link QNodeTypeDefinitionBuilder}.
+     */
+    public class QNodeTypeDefinitionBuilderImpl extends QNodeTypeDefinitionBuilder {
+
+        public QNodeDefinitionBuilder newQNodeDefinitionBuilder() {
+            return new QNodeDefinitionBuilderImpl();
+        }
+
+        public QPropertyDefinitionBuilder newQPropertyDefinition() {
+            return new QPropertyDefinitionBuilderImpl();
+        }
+
+        public QNodeTypeDefinition build() {
+            return new QNodeTypeDefinitionImpl(
+                    this.getName(),
+                    this.getSuperTypes(),
+                    this.getMixin(),
+                    this.getOrderableChildNodes(),
+                    this.getPrimaryItemName(),
+                    this.getPropertyDefs(),
+                    this.getChildNodeDefs());
+        }
+
+    }
+
+    /**
+     * Default implementation of a {@link QPropertyDefinitionBuilder}.
+     */
+    public class QPropertyDefinitionBuilderImpl extends QPropertyDefinitionBuilder {
+
+        public QValue createValue(String value, NamePathResolver resolver)
+                throws ValueFormatException, RepositoryException {
+
+            return ValueFormat.getQValue(value, getRequiredType(), resolver, QValueFactoryImpl
+                    .getInstance());
+        }
+
+        public String createValueConstraint(String constraint, NamePathResolver resolver)
+                throws InvalidConstraintException {
+
+            return ValueConstraint.create(getRequiredType(), constraint, resolver).getQualifiedDefinition();
+        }
+
+        public QPropertyDefinition build() {
+            return new QPropertyDefinitionImpl(
+                    this.getName(),
+                    this.getDeclaringNodeType(),
+                    this.getAutoCreated(),
+                    this.getMandatory(),
+                    this.getOnParentVersion(),
+                    this.getProtected(),
+                    this.getDefaultValues(),
+                    this.getMultiple(),
+                    this.getRequiredType(),
+                    this.getValueConstraints());
+        }
+
+    }
+
+    /**
+     * Default implementation of a {@link QNodeDefinitionBuilder}.
+     */
+    public class QNodeDefinitionBuilderImpl extends QNodeDefinitionBuilder {
+
+        public QNodeDefinition build() {
+            return new QNodeDefinitionImpl(
+                    this.getName(),
+                    this.getDeclaringNodeType(),
+                    this.getAutoCreated(),
+                    this.getMandatory(),
+                    this.getOnParentVersion(),
+                    this.getProtected(),
+                    this.getDefaultPrimaryType(),
+                    this.getRequiredPrimaryTypes(),
+                    this.getAllowsSameNameSiblings());
+        }
+
+    }
+
+}
\ No newline at end of file

Property changes on: jackrabbit-spi-commons\src\main\java\org\apache\jackrabbit\spi\commons\nodetype\compact\QNodeTypeDefinitionsBuilderImpl.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url
Name: svn:eol-style
   + native

Index: jackrabbit-spi-commons/pom.xml
===================================================================
--- jackrabbit-spi-commons/pom.xml	(revision 644250)
+++ jackrabbit-spi-commons/pom.xml	(working copy)
@@ -209,6 +209,11 @@
       <artifactId>slf4j-api</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java	(revision 643760)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java	(working copy)
@@ -18,11 +18,11 @@
 
 import org.apache.jackrabbit.jcr2spi.hierarchy.PropertyEntry;
 import org.apache.jackrabbit.jcr2spi.nodetype.ItemDefinitionProvider;
-import org.apache.jackrabbit.jcr2spi.nodetype.ValueConstraint;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.PropertyInfo;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
 import org.apache.jackrabbit.spi.QValue;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java	(revision 643760)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java	(working copy)
@@ -16,26 +16,27 @@
  */
 package org.apache.jackrabbit.jcr2spi.nodetype;
 
-import org.apache.jackrabbit.spi.commons.conversion.NameException;
-import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.jcr2spi.ManagerProvider;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
 import org.apache.jackrabbit.spi.QPropertyDefinition;
-import org.apache.jackrabbit.spi.QNodeTypeDefinition;
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.QValueFactory;
+import org.apache.jackrabbit.spi.commons.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
+import org.apache.jackrabbit.spi.commons.value.ValueFormat;
 import org.apache.jackrabbit.value.ValueHelper;
-import org.apache.jackrabbit.spi.commons.value.ValueFormat;
-import org.apache.jackrabbit.jcr2spi.ManagerProvider;
-import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
 
+import javax.jcr.NamespaceException;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
-import javax.jcr.NamespaceException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeDefinition;
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java	(revision 643760)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.spi.commons.value.ValueFormat;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/ValueConstraint.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/ValueConstraint.java	(revision 643760)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/ValueConstraint.java	(working copy)
@@ -1,911 +0,0 @@
-/*
- * 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.jcr2spi.nodetype;
-
-import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
-import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
-import org.apache.jackrabbit.spi.commons.conversion.NameResolver;
-import org.apache.jackrabbit.spi.commons.conversion.NameException;
-import org.apache.jackrabbit.spi.commons.conversion.PathResolver;
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.QPropertyDefinition;
-import org.apache.jackrabbit.spi.QValue;
-import org.apache.jackrabbit.value.DateValue;
-import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
-import org.apache.jackrabbit.spi.commons.name.PathFactoryImpl;
-import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
-
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.ValueFormatException;
-import javax.jcr.NamespaceException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import java.util.Calendar;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/**
- * <code>ValueConstraint</code> and its subclasses are used to check the
- * syntax of a value constraint and to test if a specific value satisfies
- * it.
- */
-public abstract class ValueConstraint {
-
-    protected static Logger log = LoggerFactory.getLogger(ValueConstraint.class);
-
-    public static final ValueConstraint[] EMPTY_ARRAY = new ValueConstraint[0];
-
-    private final String qualifiedDefinition;
-
-    protected ValueConstraint(String qualifiedDefinition) {
-        this.qualifiedDefinition = qualifiedDefinition;
-    }
-
-    /**
-     * For constraints that are not namespace prefix mapping sensitive this
-     * method returns the same result as <code>{@link #getQualifiedDefinition()}</code>.
-     * <p/>
-     * Those that are namespace prefix mapping sensitive (e.g.
-     * <code>NameConstraint</code>, <code>PathConstraint</code> and
-     * <code>ReferenceConstraint</code>) use the given <code>nsResolver</code>
-     * to reflect the current mapping in the returned value.
-     * In other words: subclasses, that need to make a conversion to JCR value
-     * must overwrite this and return a value that has all qualified names
-     * and path elements resolved.
-     *
-     * @return the definition of this constraint.
-     * @see #getQualifiedDefinition()
-     * @param resolver
-     */
-    public String getDefinition(NamePathResolver resolver) {
-        return qualifiedDefinition;
-    }
-
-    /**
-     * By default the qualified definition is the same as the JCR definition.
-     *
-     * @return the qualified definition String
-     * @see #getDefinition(NamePathResolver)
-     */
-    public String getQualifiedDefinition() {
-        return qualifiedDefinition;
-    }
-
-    /**
-     *
-     * @param value
-     * @throws ConstraintViolationException
-     * @throws RepositoryException
-     */
-    abstract void check(QValue value) throws ConstraintViolationException, RepositoryException;
-
-
-    //-----------------------------------------< java.lang.Object overrides >---
-    public boolean equals(Object other) {
-        if (other == this) {
-            return true;
-        } else if (other instanceof ValueConstraint) {
-            return qualifiedDefinition.equals(((ValueConstraint) other).qualifiedDefinition);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the hashCode of the definition String
-     *
-     * @return the hashCode of the definition String
-     * @see Object#hashCode()
-     */
-    public int hashCode() {
-        return qualifiedDefinition.hashCode();
-    }
-
-    //-----------------------------------< static factory and check methods >---
-    /**
-     * Create a new <code>ValueConstraint</code> from the String representation.
-     * Note, that the definition must be in the qualified format in case the type
-     * indicates {@link PropertyType#NAME}, {@link PropertyType#PATH} or {@link PropertyType#REFERENCE}
-     *
-     * @param type
-     * @param qualifiedDefinition
-     * @return
-     * @throws InvalidConstraintException
-     */
-    public static ValueConstraint create(int type, String qualifiedDefinition)
-        throws InvalidConstraintException {
-        if (qualifiedDefinition == null) {
-            throw new IllegalArgumentException("illegal definition (null)");
-        }
-        switch (type) {
-            // constraints which are not qName senstive
-            case PropertyType.STRING:
-                return new StringConstraint(qualifiedDefinition);
-
-            case PropertyType.BOOLEAN:
-                return new BooleanConstraint(qualifiedDefinition);
-
-            case PropertyType.BINARY:
-                return new NumericConstraint(qualifiedDefinition);
-
-            case PropertyType.DATE:
-                return new DateConstraint(qualifiedDefinition);
-
-            case PropertyType.LONG:
-            case PropertyType.DOUBLE:
-                return new NumericConstraint(qualifiedDefinition);
-
-            // qName sensitive constraints: create from qualified string
-            case PropertyType.NAME:
-                return new NameConstraint(qualifiedDefinition);
-
-            case PropertyType.PATH:
-                return new PathConstraint(qualifiedDefinition);
-
-            case PropertyType.REFERENCE:
-                return new ReferenceConstraint(qualifiedDefinition);
-
-            default:
-                throw new IllegalArgumentException("unknown/unsupported target type for constraint: "
-                        + PropertyType.nameFromValue(type));
-        }
-    }
-
-    /**
-     *
-     * @param type
-     * @param definition
-     * @param resolver
-     * @return
-     * @throws InvalidConstraintException
-     */
-    public static ValueConstraint create(int type, String definition,
-                                         NamePathResolver resolver)
-            throws InvalidConstraintException {
-        if (definition == null) {
-            throw new IllegalArgumentException("Illegal definition (null) for ValueConstraint.");
-        }
-        switch (type) {
-            case PropertyType.STRING:
-                return new StringConstraint(definition);
-
-            case PropertyType.BOOLEAN:
-                return new BooleanConstraint(definition);
-
-            case PropertyType.BINARY:
-                return new NumericConstraint(definition);
-
-            case PropertyType.DATE:
-                return new DateConstraint(definition);
-
-            case PropertyType.LONG:
-            case PropertyType.DOUBLE:
-                return new NumericConstraint(definition);
-
-            case PropertyType.NAME:
-                return new NameConstraint(definition, resolver);
-
-            case PropertyType.PATH:
-                return new PathConstraint(definition, resolver);
-
-            case PropertyType.REFERENCE:
-                return new ReferenceConstraint(definition, resolver);
-
-            default:
-                throw new IllegalArgumentException("Unknown/unsupported target type for constraint: " + PropertyType.nameFromValue(type));
-        }
-    }
-
-    /**
-     * Tests if the value constraints defined in the property definition
-     * <code>pd</code> are satisfied by the the specified <code>values</code>.
-     * <p/>
-     * Note that the <i>protected</i> flag is not checked. Also note that no
-     * type conversions are attempted if the type of the given values does not
-     * match the required type as specified in the given definition.
-     *
-     * @param pd
-     * @param values
-     * @throws ConstraintViolationException
-     */
-    public static void checkValueConstraints(QPropertyDefinition pd, QValue[] values)
-            throws ConstraintViolationException, RepositoryException {
-        // check multi-value flag
-        if (!pd.isMultiple() && values != null && values.length > 1) {
-            throw new ConstraintViolationException("the property is not multi-valued");
-        }
-
-        String[] constraints = pd.getValueConstraints();
-        if (constraints == null || constraints.length == 0) {
-            // no constraints to check
-            return;
-        }
-        if (values != null && values.length > 0) {
-            // check value constraints on every value
-            for (int i = 0; i < values.length; i++) {
-                // constraints are OR-ed together
-                boolean satisfied = false;
-                ConstraintViolationException cve = null;
-                for (int j = 0; j < constraints.length && !satisfied; j++) {
-                    try {
-                        ValueConstraint cnstr = ValueConstraint.create(pd.getRequiredType(), constraints[j]);
-                        cnstr.check(values[i]);
-                        satisfied = true;
-                    } catch (ConstraintViolationException e) {
-                        cve = e;
-                    } catch (InvalidConstraintException e) {
-                        cve = new ConstraintViolationException(e.getMessage(), e);
-                    }
-                }
-                if (!satisfied) {
-                    // re-throw last exception we encountered
-                    throw cve;
-                }
-            }
-        }
-    }
-}
-
-//---------------------------------------------< Subclass BooleanConstraint >---
-/**
- * <code>BooleanConstraint</code> ...
- */
-class BooleanConstraint extends ValueConstraint {
-    final boolean reqBool;
-
-    BooleanConstraint(String definition) throws InvalidConstraintException {
-        super(definition);
-
-        // constraint format: 'true' or 'false'
-        if (definition.equals("true")) {
-            reqBool = true;
-        } else if (definition.equals("false")) {
-            reqBool = false;
-        } else {
-            String msg = "'" + definition
-                    + "' is not a valid value constraint format for BOOLEAN values";
-            log.debug(msg);
-            throw new InvalidConstraintException(msg);
-        }
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '"  + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.BOOLEAN:
-                boolean b = Boolean.valueOf(value.getString()).booleanValue();
-                if (b != reqBool) {
-                    throw new ConstraintViolationException("'" + b + "' does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-                }
-                return;
-
-            default:
-                String msg = "BOOLEAN constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//----------------------------------------------< Subclass StringConstraint >---
-/**
- * <code>StringConstraint</code> ...
- */
-class StringConstraint extends ValueConstraint {
-    final Pattern pattern;
-
-    StringConstraint(String definition) throws InvalidConstraintException {
-        super(definition);
-
-        // constraint format: regexp
-        try {
-            pattern = Pattern.compile(definition);
-        } catch (PatternSyntaxException pse) {
-            String msg = "'" + definition + "' is not valid regular expression syntax";
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, pse);
-        }
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.STRING:
-                String text = value.getString();
-                Matcher matcher = pattern.matcher(text);
-                if (!matcher.matches()) {
-                    throw new ConstraintViolationException("'" + text  + "' does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-                }
-                return;
-
-            default:
-                String msg = "STRING constraint can not be applied to value of type: " + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//---------------------------------------------< Subclass NumericConstraint >---
-/**
- * <code>NumericConstraint</code> ...
- */
-class NumericConstraint extends ValueConstraint {
-    final boolean lowerInclusive;
-    final Double lowerLimit;
-    final boolean upperInclusive;
-    final Double upperLimit;
-
-    NumericConstraint(String definition) throws InvalidConstraintException {
-        super(definition);
-
-        // format: '(<min>, <max>)',  '[<min>, <max>]', '(, <max>)' etc.
-        Pattern pattern = Pattern.compile("([\\(\\[]) *(\\-?\\d+\\.?\\d*)? *, *(\\-?\\d+\\.?\\d*)? *([\\)\\]])");
-        Matcher matcher = pattern.matcher(definition);
-        if (matcher.matches()) {
-            try {
-                // group 1 is lower inclusive/exclusive
-                String s = matcher.group(1);
-                lowerInclusive = s.equals("[");
-                // group 2 is lower limit
-                s = matcher.group(2);
-                if (s == null || s.length() == 0) {
-                    lowerLimit = null;
-                } else {
-                    lowerLimit = Double.valueOf(matcher.group(2));
-                }
-                // group 3 is upper limit
-                s = matcher.group(3);
-                if (s == null || s.length() == 0) {
-                    upperLimit = null;
-                } else {
-                    upperLimit = Double.valueOf(matcher.group(3));
-                }
-                // group 4 is lower inclusive/exclusive
-                s = matcher.group(4);
-                upperInclusive = s.equals("]");
-                if (lowerLimit == null && upperLimit == null) {
-                    String msg = "'" + definition + "' is not a valid value constraint"
-                            + " format for numeric types: neither lower- nor upper-limit specified";
-                    log.debug(msg);
-                    throw new InvalidConstraintException(msg);
-                }
-                if (lowerLimit != null && upperLimit != null) {
-                    if (lowerLimit.doubleValue() > upperLimit.doubleValue()) {
-                        String msg = "'" + definition
-                                + "' is not a valid value constraint format for numeric types: lower-limit exceeds upper-limit";
-                        log.debug(msg);
-                        throw new InvalidConstraintException(msg);
-                    }
-                }
-            } catch (NumberFormatException nfe) {
-                String msg = "'" + definition
-                        + "' is not a valid value constraint format for numeric types";
-                log.debug(msg);
-                throw new InvalidConstraintException(msg, nfe);
-            }
-        } else {
-            String msg = "'" + definition
-                    + "' is not a valid value constraint format for numeric values";
-            log.debug(msg);
-            throw new InvalidConstraintException(msg);
-        }
-    }
-
-    private void check(double number) throws ConstraintViolationException {
-        if (lowerLimit != null) {
-            if (lowerInclusive) {
-                if (number < lowerLimit.doubleValue()) {
-                    throw new ConstraintViolationException(number
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            } else {
-                if (number <= lowerLimit.doubleValue()) {
-                    throw new ConstraintViolationException(number
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            }
-        }
-        if (upperLimit != null) {
-            if (upperInclusive) {
-                if (number > upperLimit.doubleValue()) {
-                    throw new ConstraintViolationException(number
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            } else {
-                if (number >= upperLimit.doubleValue()) {
-                    throw new ConstraintViolationException(number
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            }
-        }
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '"
-                    + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.LONG:
-                check(value.getLong());
-                return;
-
-            case PropertyType.DOUBLE:
-                check(value.getDouble());
-                return;
-
-            case PropertyType.BINARY:
-                long length = value.getLength();
-                if (length != -1) {
-                    check(length);
-                } else {
-                    log.warn("failed to determine length of binary value");
-                }
-                return;
-
-            default:
-                String msg = "numeric constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//------------------------------------------------< Subclass DateConstraint >---
-/**
- * <code>DateConstraint</code> ...
- */
-class DateConstraint extends ValueConstraint {
-    final boolean lowerInclusive;
-    final Calendar lowerLimit;
-    final boolean upperInclusive;
-    final Calendar upperLimit;
-
-    DateConstraint(String definition) throws InvalidConstraintException {
-        super(definition);
-
-        // format: '(<fromDate>, <toDate>)', '[<fromDate>, <toDate>]', '[, <toDate>]' etc.
-        Pattern pattern = Pattern.compile("([\\(\\[]) *([0-9TZ\\.\\+-:]*)? *, *([0-9TZ\\.\\+-:]*)? *([\\)\\]])");
-        Matcher matcher = pattern.matcher(definition);
-        if (matcher.matches()) {
-            try {
-                // group 1 is lower inclusive/exclusive
-                String s = matcher.group(1);
-                lowerInclusive = s.equals("[");
-                // group 2 is lower limit
-                s = matcher.group(2);
-                if (s == null || s.length() == 0) {
-                    lowerLimit = null;
-                } else {
-                    lowerLimit = DateValue.valueOf(matcher.group(2)).getDate();
-                }
-                // group 3 is upper limit
-                s = matcher.group(3);
-                if (s == null || s.length() == 0) {
-                    upperLimit = null;
-                } else {
-                    upperLimit = DateValue.valueOf(matcher.group(3)).getDate();
-                }
-                // group 4 is upepr inclusive/exclusive
-                s = matcher.group(4);
-                upperInclusive = s.equals("]");
-
-                if (lowerLimit == null && upperLimit == null) {
-                    String msg = "'" + definition
-                            + "' is not a valid value constraint format for dates: neither min- nor max-date specified";
-                    log.debug(msg);
-                    throw new InvalidConstraintException(msg);
-                }
-                if (lowerLimit != null && upperLimit != null) {
-                    if (lowerLimit.after(upperLimit)) {
-                        String msg = "'" + definition
-                                + "' is not a valid value constraint format for dates: min-date > max-date";
-                        log.debug(msg);
-                        throw new InvalidConstraintException(msg);
-                    }
-                }
-            } catch (ValueFormatException vfe) {
-                String msg = "'" + definition
-                        + "' is not a valid value constraint format for dates";
-                log.debug(msg);
-                throw new InvalidConstraintException(msg, vfe);
-            } catch (RepositoryException re) {
-                String msg = "'" + definition
-                        + "' is not a valid value constraint format for dates";
-                log.debug(msg);
-                throw new InvalidConstraintException(msg, re);
-            }
-        } else {
-            String msg = "'" + definition
-                    + "' is not a valid value constraint format for dates";
-            log.debug(msg);
-            throw new InvalidConstraintException(msg);
-        }
-    }
-
-    private void check(Calendar cal) throws ConstraintViolationException {
-        if (cal == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        if (lowerLimit != null) {
-            if (lowerInclusive) {
-                if (cal.getTimeInMillis() < lowerLimit.getTimeInMillis()) {
-                    throw new ConstraintViolationException(cal
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            } else {
-                if (cal.getTimeInMillis() <= lowerLimit.getTimeInMillis()) {
-                    throw new ConstraintViolationException(cal
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            }
-        }
-        if (upperLimit != null) {
-            if (upperInclusive) {
-                if (cal.getTimeInMillis() > upperLimit.getTimeInMillis()) {
-                    throw new ConstraintViolationException(cal
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            } else {
-                if (cal.getTimeInMillis() >= upperLimit.getTimeInMillis()) {
-                    throw new ConstraintViolationException(cal
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-            }
-        }
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.DATE:
-                check(value.getCalendar());
-                return;
-
-            default:
-                String msg = "DATE constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//------------------------------------------------< Subclass PathConstraint >---
-/**
- * <code>PathConstraint</code> ...
- */
-class PathConstraint extends ValueConstraint {
-
-    final Path path;
-    final boolean deep;
-
-    PathConstraint(String qualifiedDefinition) {
-        super(qualifiedDefinition);
-        // constraint format: qualified absolute or relative path with optional trailing wildcard
-        deep = qualifiedDefinition.endsWith("*");
-        // TODO improve. don't rely on a specific factory impl
-        path = PathFactoryImpl.getInstance().create(qualifiedDefinition);
-    }
-
-    PathConstraint(String definition, PathResolver resolver)
-            throws InvalidConstraintException {
-        super(definition);
-
-        // constraint format: absolute or relative path with optional trailing wildcard
-        deep = definition.endsWith("*");
-        if (deep) {
-            // trim trailing wildcard before building path
-            definition = definition.substring(0, definition.length() - 1);
-        }
-        try {
-            path = resolver.getQPath(definition);
-        } catch (NameException e) {
-            String msg = "Invalid path expression specified as value constraint: " + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, e);
-        } catch (NamespaceException e) {
-            String msg = "Invalid path expression specified as value constraint: " + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, e);
-        }
-    }
-
-    /**
-     * Uses {@link NamePathResolver#getJCRPath(Path)} to convert the
-     * qualified <code>Path</code> into a JCR path.
-     *
-     * @see ValueConstraint#getDefinition(NamePathResolver)
-     * @param resolver
-     */
-    public String getDefinition(NamePathResolver resolver) {
-        try {
-            String p = resolver.getJCRPath(path);
-            if (!deep) {
-                return p;
-            } else if (path.denotesRoot()) {
-                return p + "*";
-            } else {
-                return p + "/*";
-            }
-        } catch (NamespaceException e) {
-            // should never get here, return raw definition as fallback
-            return getQualifiedDefinition();
-        }
-    }
-
-    /**
-     * Returns the String representation of the path.
-     *
-     * @return String representation of the path.
-     * @see ValueConstraint#getQualifiedDefinition()
-     */
-    public String getQualifiedDefinition() {
-        return path.toString();
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.PATH:
-                Path p = value.getPath();
-                // normalize paths before comparing them
-                Path p0, p1;
-                try {
-                    p0 = path.getNormalizedPath();
-                    p1 = p.getNormalizedPath();
-                } catch (RepositoryException e) {
-                    throw new ConstraintViolationException("path not valid: " + e);
-                }
-                if (deep) {
-                    try {
-                        if (!p0.isAncestorOf(p1)) {
-                            throw new ConstraintViolationException(p
-                                + " does not satisfy the constraint '"
-                                + getQualifiedDefinition() + "'");
-                        }
-                    } catch (RepositoryException e) {
-                        // can't compare relative with absolute path
-                        throw new ConstraintViolationException(p
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                    }
-                } else {
-                    // exact match required
-                    if (!p0.equals(p1)) {
-                        throw new ConstraintViolationException(p
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                    }
-                }
-                return;
-
-            default:
-                String msg = "PATH constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//------------------------------------------------< Subclass NameConstraint >---
-/**
- * <code>NameConstraint</code> ...
- */
-class NameConstraint extends ValueConstraint {
-
-    private final Name name;
-
-    NameConstraint(String qualifiedDefinition) {
-        super(qualifiedDefinition);
-        // constraint format: String representation of qualified name
-        // TODO improve. don't rely on a specific factory impl
-        name = NameFactoryImpl.getInstance().create(qualifiedDefinition);
-    }
-
-    NameConstraint(String definition, NameResolver resolver)
-            throws InvalidConstraintException {
-        super(definition);
-        // constraint format: JCR name in prefix form
-        try {
-            name = resolver.getQName(definition);
-        } catch (NameException e) {
-            String msg = "invalid name specified as value constraint: "
-                    + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, e);
-        } catch (NamespaceException e) {
-            String msg = "invalid name specified as value constraint: "
-                    + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, e);
-        }
-    }
-
-    /**
-     * Uses {@link NamePathResolver#getJCRName(Name)} to convert the
-     * qualified <code>Name</code> into a JCR name.
-     *
-     * @see ValueConstraint#getDefinition(NamePathResolver)
-     * @param resolver
-     */
-    public String getDefinition(NamePathResolver resolver) {
-        try {
-            return resolver.getJCRName(name);
-        } catch (NamespaceException e) {
-            // should never get here, return raw definition as fallback
-            return getQualifiedDefinition();
-        }
-    }
-
-    /**
-     * Returns the String representation of the qualified name
-     *
-     * @return String representation of the qualified name
-     * @see ValueConstraint#getQualifiedDefinition()
-     */
-    public String getQualifiedDefinition() {
-        return name.toString();
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.NAME:
-                Name n = value.getName();
-                if (!name.equals(n)) {
-                    throw new ConstraintViolationException(n
-                            + " does not satisfy the constraint '"
-                            + getQualifiedDefinition() + "'");
-                }
-                return;
-
-            default:
-                String msg = "NAME constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-//-------------------------------------------< Subclass ReferenceConstraint >---
-/**
- * <code>ReferenceConstraint</code> ...
- */
-class ReferenceConstraint extends ValueConstraint {
-
-    private final Name ntName;
-
-    ReferenceConstraint(String qualifiedDefinition) {
-        super(qualifiedDefinition);
-        // format: qualified node type name
-        // TODO improve. don't rely on a specific factory impl
-        ntName = NameFactoryImpl.getInstance().create(qualifiedDefinition);
-    }
-
-    ReferenceConstraint(String definition, NamePathResolver resolver) throws InvalidConstraintException {
-        super(definition);
-
-        // format: node type name
-        try {
-            ntName = resolver.getQName(definition);
-        } catch (IllegalNameException ine) {
-            String msg = "invalid node type name specified as value constraint: "
-                    + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, ine);
-        } catch (NamespaceException e) {
-            String msg = "invalid node type name specified as value constraint: "
-                    + definition;
-            log.debug(msg);
-            throw new InvalidConstraintException(msg, e);
-        }
-    }
-
-    /**
-     * Uses {@link NamePathResolver#getJCRName(Name)} to convert the
-     * qualified nodetype name into a JCR name.
-     *
-     * @see ValueConstraint#getDefinition(NamePathResolver)
-     * @param resolver
-     */
-    public String getDefinition(NamePathResolver resolver) {
-        try {
-            return resolver.getJCRName(ntName);
-        } catch (NamespaceException npde) {
-            // should never get here, return raw definition as fallback
-            return getQualifiedDefinition();
-        }
-    }
-
-    /**
-     * Returns the String representation of the qualified nodetype name.
-     *
-     * @return String representation of the qualified nodetype name.
-     */
-    public String getQualifiedDefinition() {
-        return ntName.toString();
-    }
-
-    /**
-     * @see ValueConstraint#check(QValue)
-     */
-    void check(QValue value) throws ConstraintViolationException, RepositoryException {
-        if (value == null) {
-            throw new ConstraintViolationException("Null value does not satisfy the constraint '" + getQualifiedDefinition() + "'");
-        }
-        switch (value.getType()) {
-            case PropertyType.REFERENCE:
-                // TODO check REFERENCE value constraint (requires a session)
-                log.warn("validation of REFERENCE constraint is not yet implemented");
-                return;
-
-            default:
-                String msg = "REFERENCE constraint can not be applied to value of type: "
-                        + PropertyType.nameFromValue(value.getType());
-                log.debug(msg);
-                throw new RepositoryException(msg);
-        }
-    }
-}
-
-
Index: jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
===================================================================
--- jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java	(revision 643760)
+++ jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java	(working copy)
@@ -23,6 +23,7 @@
 import org.apache.jackrabbit.spi.QValue;
 import org.apache.jackrabbit.spi.commons.nodetype.InvalidNodeTypeDefException;
 import org.apache.jackrabbit.spi.commons.nodetype.NodeTypeConflictException;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.slf4j.LoggerFactory;
