Author: rhauch
Date: 2009-03-18 13:05:16 -0400 (Wed, 18 Mar 2009)
New Revision: 783
Added:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
trunk/dna-jcr/src/test/resources/vehicles.xml
Log:
DNA-305 JcrNodeType.canSetProperty Does Not Enforce Constraints
Applied the patch, which is an interim commit to add in the constraint checking. Future
changes will remove the JcrSession references from JcrNodeTypeManager,
JcrPropertyDefinition, etc. under DNA-302.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-18 16:54:54 UTC
(rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-18 17:05:16 UTC
(rev 783)
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Set;
import java.util.Stack;
+import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeDefinition;
@@ -413,6 +414,35 @@
}
/**
+ * Returns <code>true</code> if <code>value</code> can be
cast to <code>property.getRequiredType()</code> per the type
+ * conversion rules in section 6.2.6 of the JCR 1.0 specification AND
<code>value</code> satisfies the constraints (if any)
+ * for the property definition. If the property definition has a required type of
{@link PropertyType#UNDEFINED}, the cast
+ * will be considered to have succeeded and the value constraints (if any) will be
interpreted using the semantics for the
+ * type specified in <code>value.getType()</code>.
+ *
+ * @param propertyDefinition the property definition to validate against
+ * @param value the value to be validated
+ * @return <code>true</code> if the value can be cast to the required
type for the property definition (if it exists) and
+ * satisfies the constraints for the property (if any exist).
+ * @see PropertyDefinition#getValueConstraints()
+ * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+ */
+ private boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition
propertyDefinition,
+ Value value ) {
+ try {
+ assert value instanceof JcrValue : "Illegal implementation of Value
interface";
+ ((JcrValue)value).asType(propertyDefinition.getRequiredType());
+
+ return propertyDefinition.satisfiesConstraints(value);
+
+ } catch (javax.jcr.ValueFormatException vfe) {
+ // Cast failed
+ return false;
+ }
+
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String,
javax.jcr.Value)
@@ -440,14 +470,7 @@
return !property.isMandatory();
}
- try {
- assert value instanceof JcrValue : "Illegal implementation of Value
interface";
- ((JcrValue)value).asType(property.getRequiredType());
- } catch (javax.jcr.ValueFormatException vfe) {
- // Cast failed
- return false;
- }
- return true;
+ return canCastToTypeAndMatchesConstraints(property, value);
}
/**
@@ -480,11 +503,7 @@
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
- try {
- assert values[i] instanceof JcrValue : "Illegal implementation
of Value interface";
- ((JcrValue)values[i]).asType(property.getRequiredType());
- } catch (javax.jcr.ValueFormatException vfe) {
- // Cast failed
+ if (!canCastToTypeAndMatchesConstraints(property, values[i])) {
return false;
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-18
16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-18
17:05:16 UTC (rev 783)
@@ -23,11 +23,24 @@
*/
package org.jboss.dna.jcr;
+import java.util.regex.Pattern;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
-
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.JodaDateTime;
/**
* DNA implementation of the {@link PropertyDefinition} interface. This implementation is
immutable and has all fields initialized
* through its constructor.
@@ -40,6 +53,7 @@
private final String[] valueConstraints;
private final boolean multiple;
private PropertyDefinitionId id;
+ private ConstraintChecker checker = null;
JcrPropertyDefinition( JcrSession session,
JcrNodeType declaringNodeType,
@@ -120,4 +134,432 @@
this.isAutoCreated(), this.isMandatory(),
this.isProtected(), this.getDefaultValues(),
this.getRequiredType(),
this.getValueConstraints(), this.isMultiple());
}
+
+ boolean satisfiesConstraints( Value value ) {
+ assert value instanceof JcrValue;
+
+ if (valueConstraints == null || valueConstraints.length == 0) {
+ return true;
+ }
+
+ // Neither the 1.0 or 2.0 specification formally prohibit constraints on
properties with no required type.
+ int type = requiredType == PropertyType.UNDEFINED ? value.getType() :
requiredType;
+
+ /*
+ * Keep a method-local reference to the constraint checker in case another thread
attempts to concurrently
+ * check the constraints with a different required type.
+ */
+ ConstraintChecker checker = this.checker;
+
+ if (checker == null || checker.getType() != type) {
+ checker = createChecker(session, type, valueConstraints);
+ this.checker = checker;
+ }
+
+ try {
+ return checker.matches(value);
+ } catch (ValueFormatException vfe) {
+ // The value was so wonky that we couldn't even convert it to an
appropriate type
+ return false;
+ }
+ }
+
+ /**
+ * Returns a {@link ConstraintChecker} that will interpret the constraints described
by <code>valueConstraints</code> using
+ * the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type
indicated by <code>type</code> (where
+ * <code>type</code> is a value from {@link PropertyType}) for the given
<code>session</code>. The session is required to
+ * handle the UUID lookups for reference constraints and provides the {@link
ExecutionContext} that is used to provide
+ * namespace mappings and value factories for the other constraint checkers.
+ *
+ * @param session the current session
+ * @param type the type of constraint checker that should be created (based on values
from {@link PropertyType}).
+ * Type-specific semantics are defined in section 6.7.16 of the JCR 1.0
specification.
+ * @param valueConstraints the constraints for the node as provided by {@link
PropertyDefinition#getValueConstraints()}.
+ * @return a constraint checker that matches the given parameters
+ */
+ private ConstraintChecker createChecker( JcrSession session,
+ int type,
+ String[] valueConstraints ) {
+ ExecutionContext context = session.getExecutionContext();
+
+ switch (type) {
+ case PropertyType.BINARY:
+ return new BinaryConstraintChecker(valueConstraints, context);
+ case PropertyType.DATE:
+ return new DateTimeConstraintChecker(valueConstraints, context);
+ case PropertyType.DOUBLE:
+ return new DoubleConstraintChecker(valueConstraints, context);
+ case PropertyType.LONG:
+ return new LongConstraintChecker(valueConstraints, context);
+ case PropertyType.NAME:
+ return new NameConstraintChecker(valueConstraints, context);
+ case PropertyType.PATH:
+ return new PathConstraintChecker(valueConstraints, context);
+ case PropertyType.REFERENCE:
+ return new ReferenceConstraintChecker(valueConstraints, session);
+ case PropertyType.STRING:
+ return new StringConstraintChecker(valueConstraints, context);
+ default:
+ throw new IllegalStateException("Invalid property type: " +
type);
+ }
+ }
+
+ /**
+ * Interface that encapsulates a reusable method that can test values to determine if
they match a specific list of
+ * constraints for the semantics associated with a single {@link PropertyType}.
+ */
+ public interface ConstraintChecker {
+
+ /**
+ * Returns the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that
defines the semantics used for interpretation
+ * for the constraint values.
+ *
+ * @return the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that
defines the semantics used for interpretation
+ * for the constraint values
+ */
+ public abstract int getType();
+
+ /**
+ * Returns <code>true</code> if and only if
<code>value</code> satisfies the constraints used to create this constraint
+ * checker.
+ *
+ * @param value the value to test
+ * @return whether or not the value satisfies the constraints used to create this
constraint checker
+ * @see PropertyDefinition#getValueConstraints()
+ * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+ */
+ public abstract boolean matches( Value value );
+ }
+
+ private interface Range<T> {
+ boolean accepts( T value );
+ }
+
+ /**
+ * Encapsulation of common parsing logic used for all ranged constraints. Binary,
long, double, and date values all have their
+ * constraints interpreted as a set of ranges that may include or exclude each
end-point in the range.
+ *
+ * @param <T> the specific type of the constraint (e.g., Binary, Long, Double,
or DateTime).
+ */
+ private static abstract class RangeConstraintChecker<T extends
Comparable<T>> implements ConstraintChecker {
+ private final Range<T>[] constraints;
+ private final ValueFactory<T> valueFactory;
+
+ @SuppressWarnings( "unchecked" )
+ protected RangeConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ constraints = new Range[valueConstraints.length];
+ this.valueFactory = getValueFactory(context.getValueFactories());
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = parseValueConstraint(valueConstraints[i]);
+ }
+ }
+
+ protected abstract ValueFactory<T> getValueFactory( ValueFactories
valueFactories );
+
+ protected abstract Comparable<T> parseValue( String s );
+
+ /**
+ * Parses one constraint value into a {@link Range} that will accept only values
which match the range described by the
+ * value constraint.
+ *
+ * @param valueConstraint the individual value constraint to be parsed into a
{@link Range}.
+ * @return a range that accepts values which match the given value constraint.
+ */
+ private Range<T> parseValueConstraint( String valueConstraint ) {
+ assert valueConstraint != null;
+
+ final boolean includeLower = valueConstraint.charAt(0) == '[';
+ final boolean includeUpper = valueConstraint.charAt(valueConstraint.length()
- 1) == ']';
+
+ int commaInd = valueConstraint.indexOf(',');
+ String lval = commaInd > 1 ? valueConstraint.substring(1, commaInd) :
null;
+ String rval = commaInd < valueConstraint.length() - 2 ?
valueConstraint.substring(commaInd + 1,
+
valueConstraint.length() - 1) : null;
+
+ final Comparable<T> lower = lval == null ? null :
parseValue(lval.trim());
+ final Comparable<T> upper = rval == null ? null :
parseValue(rval.trim());
+
+ return new Range<T>() {
+ public boolean accepts( T value ) {
+ if (lower != null && (includeLower ? lower.compareTo(value)
> 0 : lower.compareTo(value) >= 0)) {
+ return false;
+ }
+ if (upper != null && (includeUpper ? upper.compareTo(value)
< 0 : upper.compareTo(value) <= 0)) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see ConstraintChecker#matches(javax.jcr.Value)
+ */
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ T convertedValue = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].accepts(convertedValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class BinaryConstraintChecker extends LongConstraintChecker {
+ private final ValueFactories valueFactories;
+
+ protected BinaryConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+
+ this.valueFactories = context.getValueFactories();
+ }
+
+ @Override
+ public int getType() {
+ return PropertyType.BINARY;
+ }
+
+ @Override
+ public boolean matches( Value value ) {
+ Binary binary =
valueFactories.getBinaryFactory().create(((JcrValue)value).value());
+ return super.matches(new JcrValue(valueFactories, PropertyType.LONG,
binary.getSize()));
+ }
+ }
+
+ @Immutable
+ private static class LongConstraintChecker extends RangeConstraintChecker<Long>
{
+
+ protected LongConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.LONG;
+ }
+
+ @Override
+ protected ValueFactory<Long> getValueFactory( ValueFactories valueFactories
) {
+ return valueFactories.getLongFactory();
+ }
+
+ @Override
+ protected Comparable<Long> parseValue( String s ) {
+ return Long.parseLong(s);
+ }
+ }
+
+ @Immutable
+ private static class DateTimeConstraintChecker extends
RangeConstraintChecker<DateTime> {
+
+ protected DateTimeConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.DATE;
+ }
+
+ @Override
+ protected ValueFactory<DateTime> getValueFactory( ValueFactories
valueFactories ) {
+ return valueFactories.getDateFactory();
+ }
+
+ @Override
+ protected Comparable<DateTime> parseValue( String s ) {
+ return new JodaDateTime(s.trim());
+ }
+ }
+
+ @Immutable
+ private static class DoubleConstraintChecker extends
RangeConstraintChecker<Double> {
+
+ protected DoubleConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.DOUBLE;
+ }
+
+ @Override
+ protected ValueFactory<Double> getValueFactory( ValueFactories
valueFactories ) {
+ return valueFactories.getDoubleFactory();
+ }
+
+ @Override
+ protected Comparable<Double> parseValue( String s ) {
+ return Double.parseDouble(s);
+ }
+ }
+
+ @Immutable
+ private static class ReferenceConstraintChecker implements ConstraintChecker {
+ private final JcrSession session;
+
+ private final Name[] constraints;
+
+ protected ReferenceConstraintChecker( String[] valueConstraints,
+ JcrSession session ) {
+ this.session = session;
+
+ NameFactory factory =
session.getExecutionContext().getValueFactories().getNameFactory();
+
+ constraints = new Name[valueConstraints.length];
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = factory.create(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.REFERENCE;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ Node node = null;
+ try {
+ node = session.getNodeByUUID(((JcrValue)value).value().toString());
+ } catch (RepositoryException re) {
+ return false;
+ }
+
+ for (int i = 0; i < constraints.length; i++) {
+ try {
+ if
(node.isNodeType(constraints[i].getString(session.getExecutionContext().getNamespaceRegistry())))
{
+ return true;
+ }
+ } catch (RepositoryException re) {
+ throw new IllegalStateException(re);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class NameConstraintChecker implements ConstraintChecker {
+ private final Name[] constraints;
+ private final ValueFactory<Name> valueFactory;
+
+ protected NameConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ this.valueFactory = context.getValueFactories().getNameFactory();
+
+ constraints = new Name[valueConstraints.length];
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = valueFactory.create(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.NAME;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ Name name = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class StringConstraintChecker implements ConstraintChecker {
+ private final Pattern[] constraints;
+ private ValueFactory<String> valueFactory;
+
+ protected StringConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ constraints = new Pattern[valueConstraints.length];
+ this.valueFactory = context.getValueFactories().getStringFactory();
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = Pattern.compile(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.STRING;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ String convertedValue = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].matcher(convertedValue).matches()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class PathConstraintChecker implements ConstraintChecker {
+ private final ExecutionContext context;
+ private final String[] constraints;
+
+ protected PathConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ this.constraints = valueConstraints;
+ this.context = context;
+ }
+
+ public int getType() {
+ return PropertyType.PATH;
+ }
+
+ public boolean matches( Value valueToMatch ) {
+ assert valueToMatch != null;
+
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path value = pathFactory.create(((JcrValue)valueToMatch).value());
+ value = value.getNormalizedPath();
+
+ for (int i = 0; i < constraints.length; i++) {
+ boolean matchesDescendants = constraints[i].endsWith("*");
+ Path constraintPath = pathFactory.create(matchesDescendants ?
constraints[i].substring(0,
+
constraints[i].length() - 2) : constraints[i]);
+
+ if (matchesDescendants && value.isDecendantOf(constraintPath)) {
+ return true;
+ }
+
+ if (!matchesDescendants && value.equals(constraintPath)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-18 16:54:54 UTC
(rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-18 17:05:16 UTC
(rev 783)
@@ -71,6 +71,16 @@
}
/**
+ * Returns a direct reference to the internal value object wrapped by this {@link
JcrValue}. Useful to avoid the expense of
+ * {@link #asType(int)} if the caller already knows the type of the value.
+ *
+ * @return a reference to the {@link #value} field.
+ */
+ final Object value() {
+ return value;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.Value#getBoolean()
@@ -240,8 +250,8 @@
Segment[] segments = path.getSegmentsArray();
if (!path.isAbsolute() && segments.length == 1 &&
!segments[0].hasIndex()) {
- return new JcrValue(this.valueFactories, type,
this.valueFactories.getNameFactory()
-
.create(valueAsString));
+ return new JcrValue(this.valueFactories, type,
+
this.valueFactories.getNameFactory().create(valueAsString));
}
}
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
(rev 0)
+++
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java 2009-03-18
17:05:16 UTC (rev 783)
@@ -0,0 +1,629 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * Indirectly tests the JcrConstaintCheckerFactory through {@link
JcrPropertyDefinition#satisfiesConstraints(Value)},
+ * which provides the wrapper around the factory that the rest of the API is expected to
utilize.
+ *
+ */
+public class JcrPropertyDefinitionTest {
+
+ private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] { "[,5)",
"[10, 20)", "(30,40]", "[50,]" };
+ private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] {
"[,+1945-08-01T01:30:00.000Z]", "[+1975-08-01T01:30:00.000Z,)"};
+ private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] {
"[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]"
};
+ private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] { "[,5)",
"[10, 20)", "(30,40]", "[50,]" };
+ private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] {
"jcr:system", "dnatest:constrainedType" };
+ //private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" +
JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c" };
+ private final String[] EXPECTED_PATH_CONSTRAINTS = new String[]
{"/jcr:system/*", "b", "/a/b/c" };
+ private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] {
"dna:root" };
+ private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] { "foo",
"bar*", ".*baz" };
+
+ private String workspaceName;
+ private ExecutionContext context;
+ private InMemoryRepositorySource source;
+ private JcrWorkspace workspace;
+ private JcrSession session;
+ private Graph graph;
+ private RepositoryConnectionFactory connectionFactory;
+ private NodeTypeManager nodeTypeManager;
+ private Map<String, Object> sessionAttributes;
+ @Mock
+ private JcrRepository repository;
+
+ @Before
+ public void beforeEach() throws Exception {
+ workspaceName = "workspace1";
+ final String repositorySourceName = "repository";
+
+ // Set up the source ...
+ source = new InMemoryRepositorySource();
+ source.setName(workspaceName);
+ source.setDefaultWorkspaceName(workspaceName);
+
+ // Set up the execution context ...
+ context = new ExecutionContext();
+ // Register the test namespace
+ context.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX,
TestLexicon.Namespace.URI);
+
+ // Set up the initial content ...
+ graph = Graph.create(source, context);
+
+ // Make sure the path to the namespaces exists ...
+
graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
+
graph.create("/a").and().create("/a/b").and().create("/a/b/c");
+
+
graph.set("jcr:mixinTypes").on("/a").to(JcrMixLexicon.REFERENCEABLE);
+
+ // Stub out the connection factory ...
+ connectionFactory = new RepositoryConnectionFactory() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
+ */
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnection createConnection( String sourceName ) throws
RepositorySourceException {
+ return repositorySourceName.equals(sourceName) ? source.getConnection() :
null;
+ }
+ };
+
+ // Stub out the repository, since we only need a few methods ...
+ MockitoAnnotations.initMocks(this);
+ stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
+ stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+
+ // Set up the session attributes ...
+ sessionAttributes = new HashMap<String, Object>();
+ sessionAttributes.put("attribute1", "value1");
+
+ // Now create the workspace ...
+ workspace = new JcrWorkspace(repository, workspaceName, context,
sessionAttributes);
+
+ // Create the session and log in ...
+ session = (JcrSession)workspace.getSession();
+
+ nodeTypeManager = new JcrNodeTypeManager(this.session,
+ new TestNodeTypeSource(this.session,
+ new DnaBuiltinNodeTypeSource(this.session,
+ new
JcrBuiltinNodeTypeSource(this.session))));
+ stub(workspace.getNodeTypeManager()).toReturn(nodeTypeManager);
+
+ }
+
+ @After
+ public void after() throws Exception {
+ if (session.isLive()) {
+ session.logout();
+ }
+ }
+
+ private JcrPropertyDefinition propertyDefinitionFor(NodeType nodeType, Name
propertyName) {
+ PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+ String property = propertyName.getString(context.getNamespaceRegistry());
+
+ for (int i = 0; i < propertyDefs.length; i++) {
+ if (propertyDefs[i].getName().equals(property)) {
+ return (JcrPropertyDefinition) propertyDefs[i];
+ }
+ }
+ throw new IllegalStateException("Could not find property definition name
" + property + " for type "
+ + nodeType.getName() + ". Test setup is
invalid.");
+ }
+
+ private void checkConstraints(NodeType nodeType, Name propertyName, String[]
expectedConstraints) {
+ PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+ String property = propertyName.getString(context.getNamespaceRegistry());
+ String[] constraints = null;
+
+ for (int i = 0; i < propertyDefs.length; i++) {
+ if (propertyDefs[i].getName().equals(property)) {
+ constraints = propertyDefs[i].getValueConstraints();
+ break;
+ }
+ }
+
+ if (!Arrays.equals(constraints, expectedConstraints)) {
+ throw new IllegalStateException("Unexpected constraints for property:
" + property);
+ }
+ }
+
+ private NodeType validateTypeDefinition() throws Exception {
+ NamespaceRegistry nsr = context.getNamespaceRegistry();
+
+ NodeType constrainedType =
nodeTypeManager.getNodeType(TestLexicon.CONSTRAINED_TYPE.getString(nsr));
+ assertThat(constrainedType, notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE),
notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DOUBLE), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG),
notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME),
notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH),
notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_REFERENCE), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_STRING), notNullValue());
+
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_BINARY,
EXPECTED_BINARY_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DATE,
EXPECTED_DATE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DOUBLE,
EXPECTED_DOUBLE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_LONG,
EXPECTED_LONG_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_NAME,
EXPECTED_NAME_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_PATH,
EXPECTED_PATH_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_REFERENCE,
EXPECTED_REFERENCE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_STRING,
EXPECTED_STRING_CONSTRAINTS);
+
+ return constrainedType;
+ }
+
+ private Value valueFor(Object value, int jcrType) {
+ return new JcrValue(context.getValueFactories(), jcrType, value);
+ }
+
+ private String stringOfLength(int length) {
+ StringBuffer buff = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ buff.append(i % 10);
+ }
+
+ return buff.toString();
+ }
+
+ private boolean satisfiesConstraints(JcrPropertyDefinition property, Value[] values)
{
+ for (int i = 0; i < values.length; i++) {
+ if (!property.satisfiesConstraints(values[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Test( expected = AssertionError.class )
+ public void shouldNotAllowNullValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY);
+
+ assertThat(prop.satisfiesConstraints((Value)null), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidBinaryValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the
platform's default encoding
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(0),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(4),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(10),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(19),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(31),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(40),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(50),
PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(99),
PropertyType.BINARY)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidBinaryValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the
platform's default encoding
+ Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+ valueFor(stringOfLength(10), PropertyType.BINARY),
valueFor(stringOfLength(19), PropertyType.BINARY),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(0),
PropertyType.BINARY)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidBinaryValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the
platform's default encoding
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(5),
PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(9),
PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(20),
PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(30),
PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(41),
PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(49),
PropertyType.BINARY)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidBinaryValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the
platform's default encoding
+ Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+ valueFor(stringOfLength(10), PropertyType.BINARY),
valueFor(stringOfLength(20), PropertyType.BINARY),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(9),
PropertyType.BINARY)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidDateValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DATE);
+
+
assertThat(prop.satisfiesConstraints(valueFor("-1945-08-01T01:30:00.000",
PropertyType.DATE)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("+1945-07-31T01:30:00.000",
PropertyType.DATE)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("+0001-08-01T01:30:00.000",
PropertyType.DATE)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:30:00.000",
PropertyType.DATE)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:31:00.000",
PropertyType.DATE)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("+2009-08-01T01:30:00.000",
PropertyType.DATE)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidDateValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DATE);
+
+ Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000",
PropertyType.DATE),
+ valueFor("+2009-08-01T01:30:00.000", PropertyType.DATE),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[]
{valueFor("+1975-08-01T01:31:00.000", PropertyType.DATE)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDateValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DATE);
+
+
assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.001Z",
PropertyType.DATE)), is(false));
+
assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:29:59.999Z",
PropertyType.DATE)), is(false));
+
assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.000-05:00",
PropertyType.DATE)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDateValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DATE);
+
+ Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000",
PropertyType.DATE),
+ valueFor("+1945-08-01T01:30:00.000-05:00", PropertyType.DATE),};
+ assertThat(satisfiesConstraints(prop, new Value[]
{valueFor("+1945-08-01T01:30:00.001Z", PropertyType.DATE)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidDoubleValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DOUBLE);
+
+ assertThat(prop.satisfiesConstraints(valueFor(Double.MIN_VALUE,
PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(20.19, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(30.31, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(40.4, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(50.5, PropertyType.DOUBLE)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(Double.MAX_VALUE,
PropertyType.DOUBLE)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidDoubleValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DOUBLE);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19,
PropertyType.DOUBLE),
+ valueFor(50.5, PropertyType.DOUBLE)};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99,
PropertyType.DOUBLE)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDoubleValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DOUBLE);
+
+ assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.DOUBLE)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(9.99999999, PropertyType.DOUBLE)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(20.2, PropertyType.DOUBLE)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(30.3, PropertyType.DOUBLE)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(40.41, PropertyType.DOUBLE)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(49.9, PropertyType.DOUBLE)),
is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDoubleValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_DOUBLE);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19,
PropertyType.DOUBLE),
+ valueFor(50.49, PropertyType.DOUBLE)};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(20.2,
PropertyType.DOUBLE)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidLongValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_LONG);
+
+ assertThat(prop.satisfiesConstraints(valueFor(Long.MIN_VALUE,
PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(19, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(31, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(40, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(50, PropertyType.LONG)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(Long.MAX_VALUE,
PropertyType.LONG)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidLongValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_LONG);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10,
PropertyType.LONG),
+ valueFor(50, PropertyType.LONG)};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99,
PropertyType.LONG)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidLongValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_LONG);
+
+ assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.LONG)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(9, PropertyType.LONG)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(20, PropertyType.LONG)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(30, PropertyType.LONG)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(41, PropertyType.LONG)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(49, PropertyType.LONG)),
is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidLongValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_LONG);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10,
PropertyType.LONG),
+ valueFor(49, PropertyType.LONG)};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(30,
PropertyType.LONG)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidNameValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_NAME);
+
+ assertThat(prop.satisfiesConstraints(valueFor("jcr:system",
PropertyType.NAME)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType",
PropertyType.NAME)), is(true));
+
+ // Test that names work across namespace remaps
+ session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+
assertThat(prop.satisfiesConstraints(valueFor("newprefix:constrainedType",
PropertyType.NAME)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidNameValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_NAME);
+
+ Value[] values = new Value[] {valueFor("jcr:system",
PropertyType.NAME),
+ valueFor("dnatest:constrainedType", PropertyType.NAME),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[]
{valueFor("jcr:system", PropertyType.NAME)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidNameValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_NAME);
+
+ assertThat(prop.satisfiesConstraints(valueFor("system",
PropertyType.NAME)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("jcr:system2",
PropertyType.NAME)), is(false));
+
+ // Test that old names fail after namespace remaps
+ session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+
assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType",
PropertyType.NAME)), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidNameValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_NAME);
+
+ Value[] values = new Value[] {valueFor("jcr:system",
PropertyType.NAME),
+ valueFor("dnatest:constrainedType2", PropertyType.NAME),};
+ assertThat(satisfiesConstraints(prop, new Value[]
{valueFor("jcr:system2", PropertyType.NAME)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidStringValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_STRING);
+
+ assertThat(prop.satisfiesConstraints(valueFor("foo",
PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("bar",
PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("barr",
PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("barrrrrrrrr",
PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("baz",
PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("shabaz",
PropertyType.STRING)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidStringValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_STRING);
+
+ Value[] values = new Value[] {valueFor("foo", PropertyType.STRING),
valueFor("barr", PropertyType.STRING),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("baz",
PropertyType.STRING)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidStringValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_STRING);
+
+ assertThat(prop.satisfiesConstraints(valueFor("",
PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("foot",
PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("abar",
PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("bard",
PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("baz!",
PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("bazzat",
PropertyType.STRING)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidStringValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_STRING);
+
+ Value[] values = new Value[] {valueFor("foo", PropertyType.STRING),
valueFor("bard", PropertyType.STRING),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("bazzat",
PropertyType.STRING)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidPathValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_PATH);
+
+ assertThat(prop.satisfiesConstraints(valueFor("b", PropertyType.PATH)),
is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c",
PropertyType.PATH)), is(true));
+
assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:namespace",
PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/",
PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:foo",
PropertyType.PATH)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidPathValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_PATH);
+
+ Value[] values = new Value[] {valueFor("b", PropertyType.PATH),
valueFor("/a/b/c", PropertyType.PATH),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a/b/c",
PropertyType.PATH)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidPathValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_PATH);
+
+ assertThat(prop.satisfiesConstraints(valueFor("a", PropertyType.PATH)),
is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b",
PropertyType.PATH)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/jcr:system",
PropertyType.PATH)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/d",
PropertyType.PATH)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidPathValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_PATH);
+
+ Value[] values = new Value[] {valueFor("b", PropertyType.PATH),
valueFor("/a/b/c/d", PropertyType.PATH),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a",
PropertyType.PATH)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidReferenceValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_REFERENCE);
+
+ UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+ assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)),
is(true));
+ }
+
+ @Test
+ public void shouldAllowValidReferenceValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_REFERENCE);
+
+ UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+ assertThat(satisfiesConstraints(prop, new Value[] { }), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid,
PropertyType.REFERENCE) }), is(true));
+ }
+
+
+ @Test
+ public void shouldNotAllowInvalidReferenceValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_REFERENCE);
+
+ Node aNode = session.getRootNode().getNode("a");
+ UUID uuid = ((JcrNode) aNode).nodeUuid;
+
+
+ assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)),
is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidReferenceValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType,
TestLexicon.CONSTRAINED_REFERENCE);
+
+ Node aNode = session.getRootNode().getNode("a");
+ UUID uuid = ((JcrNode) aNode).nodeUuid;
+
+ assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid,
PropertyType.REFERENCE) }), is(false));
+ }
+
+}
Property changes on:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
(rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java 2009-03-18 17:05:16 UTC
(rev 783)
@@ -0,0 +1,50 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * Namespace and names for DNA testing.
+ *
+ */
+public class TestLexicon {
+
+ public static class Namespace {
+ public static final String URI = "http://www.jboss.org/dna/test/1.0";
+ public static final String PREFIX = "dnatest";
+ }
+
+ public static final Name CONSTRAINED_TYPE = new BasicName(Namespace.URI,
"constrainedType");
+ public static final Name CONSTRAINED_BINARY = new BasicName(Namespace.URI,
"constrainedBinary");
+ public static final Name CONSTRAINED_DATE = new BasicName(Namespace.URI,
"constrainedDate");
+ public static final Name CONSTRAINED_DOUBLE = new BasicName(Namespace.URI,
"constrainedDouble");
+ public static final Name CONSTRAINED_LONG = new BasicName(Namespace.URI,
"constrainedLong");
+ public static final Name CONSTRAINED_NAME = new BasicName(Namespace.URI,
"constrainedName");
+ public static final Name CONSTRAINED_PATH = new BasicName(Namespace.URI,
"constrainedPath");
+ public static final Name CONSTRAINED_REFERENCE = new BasicName(Namespace.URI,
"constrainedReference");
+ public static final Name CONSTRAINED_STRING = new BasicName(Namespace.URI,
"constrainedString");
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
(rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java 2009-03-18
17:05:16 UTC (rev 783)
@@ -0,0 +1,145 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.nodetype.NodeType;
+
+/**
+ * Node type source with additional node types that can be used for testing. This class
defines its own namespace for its types.
+ */
+public class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+ /** The list of primary node types. */
+ private final List<JcrNodeType> primaryNodeTypes;
+ /** The list of mixin node types. */
+ private final List<JcrNodeType> mixinNodeTypes;
+
+ TestNodeTypeSource( JcrSession session,
+ JcrNodeTypeSource predecessor ) {
+ super(predecessor);
+
+ primaryNodeTypes = new ArrayList<JcrNodeType>();
+ mixinNodeTypes = new ArrayList<JcrNodeType>();
+
+ JcrNodeType base = findType(JcrNtLexicon.BASE);
+
+ if (base == null) {
+ String baseTypeName =
JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
+ String namespaceTypeName =
DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+ throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName,
namespaceTypeName));
+ }
+
+ // Stubbing in child node and property definitions for now
+ JcrNodeType constrainedType = new JcrNodeType(
+ session,
+ TestLexicon.CONSTRAINED_TYPE,
+ Arrays.asList(new NodeType[]
{base}),
+ NO_PRIMARY_ITEM_NAME,
+ NO_CHILD_NODES,
+ Arrays.asList(new
JcrPropertyDefinition[] {
+ new
JcrPropertyDefinition(session, null,
+
TestLexicon.CONSTRAINED_BINARY,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.BINARY, new String[] {"[,5)",
+
"[10, 20)", "(30,40]", "[50,]"}, false),
+ new
JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_DATE,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.DATE, new String[] {
+
"[,+1945-08-01T01:30:00.000Z]",
+
"[+1975-08-01T01:30:00.000Z,)"}, false),
+ new
JcrPropertyDefinition(session, null,
+
TestLexicon.CONSTRAINED_DOUBLE,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.DOUBLE,
+ new
String[] {"[,5.0)", "[10.1, 20.2)",
+
"(30.3,40.4]", "[50.5,]"}, false),
+
+ new
JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_LONG,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.LONG, new String[] {"[,5)",
+
"[10, 20)", "(30,40]", "[50,]"}, false),
+
+ new
JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_NAME,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.NAME, new String[] {
+
"jcr:system", "dnatest:constrainedType"},
+
false),
+
+ new
JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_PATH,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.PATH, new String[] {
+ // "/" +
JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"},
false),
+ "/jcr:system/*",
"b", "/a/b/c"}, false),
+ new
JcrPropertyDefinition(session, null,
+
TestLexicon.CONSTRAINED_REFERENCE,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.REFERENCE,
+ new
String[] {"dna:root",}, false),
+
+ new
JcrPropertyDefinition(session, null,
+
TestLexicon.CONSTRAINED_STRING,
+
OnParentVersionBehavior.IGNORE.getJcrValue(),
+
false, false, false, NO_DEFAULT_VALUES,
+
PropertyType.STRING, new String[] {"foo",
+
"bar*", ".*baz",}, false),
+
+ }), NOT_MIXIN,
UNORDERABLE_CHILD_NODES);
+
+ primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
+ mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+ return primaryNodeTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+ return mixinNodeTypes;
+ }
+
+}
Property changes on:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/resources/vehicles.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/vehicles.xml 2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/test/resources/vehicles.xml 2009-03-18 17:05:16 UTC (rev 783)
@@ -27,7 +27,7 @@
<Vehicles
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns="http://example.com/vehicles">
<Cars>
<Hybrid>
- <car jcr:name="Toyota Prius" maker="Toyota"
model="Prius" year="2008" msrp="$21,500"
userRating="4.2" valueRating="5" mpgCity="48"
mpgHighway="45"/>
+ <car jcr:name="Toyota Prius"
jcr:mixinTypes="mix:referenceable" maker="Toyota"
model="Prius" year="2008" msrp="$21,500"
userRating="4.2" valueRating="5" mpgCity="48"
mpgHighway="45"/>
<car jcr:name="Toyota Highlander" maker="Toyota"
model="Highlander" year="2008" msrp="$34,200"
userRating="4" valueRating="5" mpgCity="27"
mpgHighway="25"/>
<car jcr:name="Nissan Altima" maker="Nissan"
model="Altima" year="2008" msrp="$18,260"
mpgCity="23" mpgHighway="32"/>
</Hybrid>