You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2008/04/04 17:55:39 UTC
svn commit: r644745 [1/3] - in /jackrabbit/trunk:
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/
jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/
jackrabbit-spi-commons/ jackrabbit-spi-commons/src/main/jav...
Author: angela
Date: Fri Apr 4 08:55:26 2008
New Revision: 644745
URL: http://svn.apache.org/viewvc?rev=644745&view=rev
Log:
JCR-1516: Add Compact Namespace and Node Type Definition support to spi-commons
Added:
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefReader.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefWriter.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/Lexer.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/ParseException.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilder.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/compact/QNodeTypeDefinitionsBuilderImpl.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/BooleanConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/DateConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NameConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/NumericConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/PathConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ReferenceConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/StringConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/TestAll.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraintTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/CompactNodeTypeDefTest.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/nodetype/compact/TestAll.java (with props)
jackrabbit/trunk/jackrabbit-spi-commons/src/test/resources/
jackrabbit/trunk/jackrabbit-spi-commons/src/test/resources/cnd-reader-test-input.cnd
Removed:
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/ValueConstraint.java
Modified:
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java
jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
jackrabbit/trunk/jackrabbit-spi-commons/pom.xml
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java?rev=644745&r1=644744&r2=644745&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/DefinitionValidator.java Fri Apr 4 08:55:26 2008
@@ -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;
@@ -568,4 +569,4 @@
nsRegistry.getPrefix(name.getNamespaceURI());
}
}
-}
\ No newline at end of file
+}
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java?rev=644745&r1=644744&r2=644745&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/NodeTypeImpl.java Fri Apr 4 08:55:26 2008
@@ -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.QPropertyDefinition;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.QValueFactory;
-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.conversion.NameException;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
-import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.spi.commons.nodetype.ValueConstraint;
+import org.apache.jackrabbit.spi.commons.value.ValueFormat;
+import org.apache.jackrabbit.value.ValueHelper;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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;
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java?rev=644745&r1=644744&r2=644745&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/nodetype/PropertyDefinitionImpl.java Fri Apr 4 08:55:26 2008
@@ -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;
@@ -118,4 +119,5 @@
return ((QPropertyDefinition) itemDef).isMultiple();
}
}
+
Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java?rev=644745&r1=644744&r2=644745&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java Fri Apr 4 08:55:26 2008
@@ -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;
Modified: jackrabbit/trunk/jackrabbit-spi-commons/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/pom.xml?rev=644745&r1=644744&r2=644745&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/pom.xml (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/pom.xml Fri Apr 4 08:55:26 2008
@@ -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>
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java?rev=644745&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java Fri Apr 4 08:55:26 2008
@@ -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;
+ }
+ }
+ }
+ }
+ }
+ }
+}
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/NodeTypeDefDiff.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java?rev=644745&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java Fri Apr 4 08:55:26 2008
@@ -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);
+ }
+ }
+}
+
+
+
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/nodetype/ValueConstraint.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url