Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 15167 invoked from network); 4 Apr 2008 15:56:22 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 4 Apr 2008 15:56:22 -0000 Received: (qmail 49626 invoked by uid 500); 4 Apr 2008 15:56:22 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 49450 invoked by uid 500); 4 Apr 2008 15:56:22 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 49431 invoked by uid 99); 4 Apr 2008 15:56:22 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Apr 2008 08:56:22 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 04 Apr 2008 15:55:40 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 986101A9832; Fri, 4 Apr 2008 08:55:46 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Fri, 04 Apr 2008 15:55:39 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080404155548.986101A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 @@ slf4j-api + org.slf4j + slf4j-log4j12 + runtime + + junit junit test 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 NodeTypeDefDiff represents the result of the comparison of + * two node type definitions. + *

+ * The result of the comparison can be categorized as one of the following types: + *

+ * NONE inidcates that there is no modification at all. + *

+ * A TRIVIAL modification has no impact on the consistency + * of existing content and does not affect existing/assigned definition id's. + * The following modifications are considered TRIVIAL: + *

    + *
  • changing node type orderableChildNodes flag + *
  • changing node type primaryItemName value + *
  • adding non-mandatory property/child node + *
  • changing property/child node protected flag + *
  • changing property/child node onParentVersion value + *
  • changing property/child node mandatory flag to false + *
  • changing property/child node autoCreated flag + *
  • changing child node defaultPrimaryType + *
  • changing child node sameNameSiblings flag to true + *
  • weaken property valueConstraints (e.g. by removing completely + * or by adding to existing or by making a single constraint less restrictive) + *
  • changing property defaultValues + *
+ *

+ * A MINOR modification has no impact on the consistency + * of existing content but does affect existing/assigned definition id's. + * The following modifications are considered MINOR: + *

    + *
  • changing specific property/child node name to * + *
  • weaken child node requiredPrimaryTypes (e.g. by removing) + *
  • changing specific property requiredType to undefined + *
  • changing property multiple flag to true + *
+ *

+ * A MAJOR modification affects the consistency of + * existing content and does change existing/assigned definition id's. + * All modifications that are neither TRIVIAL nor + * MINOR are considered MAJOR. + * + * @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 + * does change existing/assigned definition id's + */ + public static final int MINOR = 2; + /** + * major modification: does affect consistency of existing content + * and does 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: + *

    + *
  • NONE: no modification at all + *
  • TRIVIAL: does neither affect consistency of + * existing content nor does it change existing/assigned definition id's + *
  • MINOR: does not affect consistency of existing + * content but does change existing/assigned definition id's + *
  • MAJOR: does affect consistency of existing + * content and does change existing/assigned definition id's + *
+ * + * @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; + +/** + * ValueConstraint 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 {@link #getQualifiedDefinition()}. + *

+ * Those that are namespace prefix mapping sensitive (e.g. + * NameConstraint, PathConstraint and + * ReferenceConstraint) use the given nsResolver + * 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 + * null 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 ValueConstraint 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 + * pd are satisfied by the the specified values. + *

+ * Note that the protected 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 >--- +/** + * BooleanConstraint ... + */ +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 >--- +/** + * StringConstraint ... + */ +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 >--- +/** + * NumericConstraint ... + */ +class NumericConstraint extends ValueConstraint { + final boolean lowerInclusive; + final Double lowerLimit; + final boolean upperInclusive; + final Double upperLimit; + + NumericConstraint(String definition) throws InvalidConstraintException { + super(definition); + + // format: '(, )', '[, ]', '(, )' 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 >--- +/** + * DateConstraint ... + */ +class DateConstraint extends ValueConstraint { + + final boolean lowerInclusive; + final Calendar lowerLimit; + final boolean upperInclusive; + final Calendar upperLimit; + + DateConstraint(String definition) throws InvalidConstraintException { + super(definition); + + // format: '(, )', '[, ]', '[, ]' 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 >--- +/** + * PathConstraint ... + */ +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 Path 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 >--- +/** + * NameConstraint ... + */ +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 Name 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 >--- +/** + * ReferenceConstraint ... + */ +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 Name 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