You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2004/09/20 18:53:40 UTC

svn commit: rev 46941 - in incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core: . nodetype xml

Author: stefan
Date: Mon Sep 20 09:53:38 2004
New Revision: 46941

Modified:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/NodeImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/Test.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/EffectiveNodeType.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeDefId.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeTypeImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/PropDefId.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/builtin_nodetypes.xml
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/DocViewImportHandler.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/SysViewImportHandler.java
Log:
differentiated handling of single vs. multi-valued properties:
multi-valued property can only be set with value array and vice versa


Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/NodeImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/NodeImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/NodeImpl.java	Mon Sep 20 09:53:38 2004
@@ -15,7 +15,6 @@
  */
 package org.apache.jackrabbit.jcr.core;
 
-import org.apache.log4j.Logger;
 import org.apache.jackrabbit.jcr.core.nodetype.*;
 import org.apache.jackrabbit.jcr.core.state.*;
 import org.apache.jackrabbit.jcr.core.version.FrozenNode;
@@ -25,6 +24,7 @@
 import org.apache.jackrabbit.jcr.util.ChildrenCollector;
 import org.apache.jackrabbit.jcr.util.IteratorHelper;
 import org.apache.jackrabbit.jcr.util.uuid.UUID;
+import org.apache.log4j.Logger;
 
 import javax.jcr.*;
 import javax.jcr.access.AccessDeniedException;
@@ -173,7 +173,7 @@
 	return genValues;
     }
 
-    protected PropertyImpl getOrCreateProperty(String name, int type)
+    protected PropertyImpl getOrCreateProperty(String name, int type, boolean multiValued)
 	    throws RepositoryException {
 	try {
 	    return (PropertyImpl) getProperty(name);
@@ -190,18 +190,18 @@
 	    throw new RepositoryException("invalid property name: " + name, upe);
 	}
 	// find definition for the specified property and create property
-	PropertyDefImpl def = getApplicablePropertyDef(qName, type);
+	PropertyDefImpl def = getApplicablePropertyDef(qName, type, multiValued);
 	return createChildProperty(qName, type, def);
     }
 
-    protected PropertyImpl getOrCreateProperty(QName name, int type)
+    protected PropertyImpl getOrCreateProperty(QName name, int type, boolean multiValued)
 	    throws RepositoryException {
 	try {
 	    return (PropertyImpl) getProperty(name);
 	} catch (ItemNotFoundException e) {
 	    // does not exist yet:
 	    // find definition for the specified property and create property
-	    PropertyDefImpl def = getApplicablePropertyDef(name, type);
+	    PropertyDefImpl def = getApplicablePropertyDef(name, type, multiValued);
 	    return createChildProperty(name, type, def);
 	}
     }
@@ -533,7 +533,7 @@
 	    prop = (PropertyImpl) itemMgr.getItem(new PropertyId(thisState.getUUID(), PROPNAME_MIXINTYPES));
 	} else {
 	    // find definition for the jcr:mixinTypes property and create property
-	    PropertyDefImpl def = getApplicablePropertyDef(PROPNAME_MIXINTYPES, PropertyType.NAME);
+	    PropertyDefImpl def = getApplicablePropertyDef(PROPNAME_MIXINTYPES, PropertyType.NAME, true);
 	    prop = createChildProperty(PROPNAME_MIXINTYPES, PropertyType.NAME, def);
 	}
 
@@ -599,13 +599,15 @@
      *
      * @param propertyName
      * @param type
+     * @param multiValued
      * @return
      * @throws RepositoryException if no applicable property definition
      *                             could be found
      */
-    protected PropertyDefImpl getApplicablePropertyDef(QName propertyName, int type)
+    protected PropertyDefImpl getApplicablePropertyDef(QName propertyName,
+						       int type, boolean multiValued)
 	    throws RepositoryException {
-	PropDef pd = getEffectiveNodeType().getApplicablePropertyDef(propertyName, type);
+	PropDef pd = getEffectiveNodeType().getApplicablePropertyDef(propertyName, type, multiValued);
 	return session.getNodeTypeManager().getPropDef(new PropDefId(pd));
     }
 
@@ -656,6 +658,7 @@
     /**
      * Checks if this node is the root node.
      * todo: is this the root node of this workspace?
+     *
      * @return
      */
     protected boolean isRepositoryRoot() {
@@ -677,11 +680,22 @@
      */
     public Property internalSetProperty(QName name, InternalValue value)
 	    throws ValueFormatException, RepositoryException {
+	// check state of this instance
+	checkItemState();
+
+	int type;
+	if (value == null) {
+	    type = PropertyType.STRING;
+	} else {
+	    type = value.getType();
+	}
+	PropertyImpl prop = getOrCreateProperty(name, type, false);
 	if (value == null) {
-	    return internalSetProperty(name, (InternalValue[]) null);
+	    prop.internalSetValue((InternalValue[]) null, type);
 	} else {
-	    return internalSetProperty(name, new InternalValue[]{value});
+	    prop.internalSetValue(new InternalValue[]{value}, type);
 	}
+	return prop;
     }
 
     /**
@@ -708,7 +722,7 @@
 	} else {
 	    type = values[0].getType();
 	}
-	PropertyImpl prop = getOrCreateProperty(name, type);
+	PropertyImpl prop = getOrCreateProperty(name, type, true);
 	prop.internalSetValue(values, type);
 	return prop;
     }
@@ -885,7 +899,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -905,7 +919,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME, true);
 	prop.setValue(values);
 	return prop;
     }
@@ -932,12 +946,34 @@
 	} else {
 	    type = values[0].getType();
 	}
-	PropertyImpl prop = getOrCreateProperty(name, type);
+	PropertyImpl prop = getOrCreateProperty(name, type, true);
 	prop.setValue(values);
 	return prop;
     }
 
     /**
+     * Same as <code>{@link Node#setProperty(String, Value)}</code> except that
+     * this method takes a <code>QName</code> name argument instead of a
+     * <code>String</code>.
+     *
+     * @param name
+     * @param value
+     * @return
+     * @throws ValueFormatException
+     * @throws RepositoryException
+     */
+    public PropertyImpl setProperty(QName name, Value value)
+	    throws ValueFormatException, RepositoryException {
+	// check state of this instance
+	checkItemState();
+
+	int type = (value == null) ? PropertyType.STRING : value.getType();
+	PropertyImpl prop = getOrCreateProperty(name, type, false);
+	prop.setValue(value);
+	return prop;
+    }
+
+    /**
      * @see ItemImpl#getQName()
      */
     public QName getQName() throws RepositoryException {
@@ -1254,7 +1290,7 @@
 	} else {
 	    type = values[0].getType();
 	}
-	PropertyImpl prop = getOrCreateProperty(name, type);
+	PropertyImpl prop = getOrCreateProperty(name, type, true);
 	prop.setValue(values);
 	return prop;
     }
@@ -1267,7 +1303,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING, true);
 	prop.setValue(values);
 	return prop;
     }
@@ -1279,7 +1315,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1293,7 +1329,7 @@
 	checkItemState();
 
 	int type = (value == null) ? PropertyType.STRING : value.getType();
-	PropertyImpl prop = getOrCreateProperty(name, type);
+	PropertyImpl prop = getOrCreateProperty(name, type, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1306,7 +1342,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.BINARY);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.BINARY, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1319,7 +1355,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.BOOLEAN);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.BOOLEAN, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1332,7 +1368,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.DOUBLE);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.DOUBLE, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1345,7 +1381,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.LONG);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.LONG, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1358,7 +1394,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.DATE);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.DATE, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -1371,7 +1407,7 @@
 	// check state of this instance
 	checkItemState();
 
-	PropertyImpl prop = getOrCreateProperty(name, PropertyType.REFERENCE);
+	PropertyImpl prop = getOrCreateProperty(name, PropertyType.REFERENCE, false);
 	prop.setValue(value);
 	return prop;
     }
@@ -2163,7 +2199,7 @@
 	    RepositoryException {
 
 	NodeImpl srcNode = getCorrespondingNode(srcWorkspaceName);
-	if (srcNode==null) {
+	if (srcNode == null) {
 	    throw new ItemNotFoundException("No corresponding node for " + safeGetJCRPath());
 	}
 	// not sure, if clone overrides 'this' node.
@@ -2173,25 +2209,26 @@
     /**
      * Returns the corresponding node in the <code>scrWorkspaceName</code> of
      * this node.
-     * <p>
+     * <p/>
      * Given a node N1 in workspace W1, its corresponding node N2 in workspace
      * W2 is defined as follows:
      * <ul>
      * <li>If N1 is the root node of W1 then N2 is the root node of W2.
      * <li>If N1 is referenceable (has a UUID) then N2 is the node in W2 with
-     *     the same UUID.
+     * the same UUID.
      * <li>If N1 is not referenceable (does not have a UUID) then there is some
-     *     node M1 which is either the nearest ancestor of N1 that is
-     *     referenceable, or is the root node of W1. If the corresponding node
-     *     of M1 is M2 in W2, then N2 is the node with the same relative path
-     *     from M2 as N1 has from M1.
+     * node M1 which is either the nearest ancestor of N1 that is
+     * referenceable, or is the root node of W1. If the corresponding node
+     * of M1 is M2 in W2, then N2 is the node with the same relative path
+     * from M2 as N1 has from M1.
      * </ul>
+     *
      * @param srcWorkspaceName
      * @return the corresponding node or <code>null</code> if no corresponding
      *         node exists.
      * @throws NoSuchWorkspaceException If <code>srcWorkspace</code> does not exist.
-     * @throws AccessDeniedException If the current session does not have sufficient rights to perform the operation.
-     * @throws RepositoryException If another error occurs.
+     * @throws AccessDeniedException    If the current session does not have sufficient rights to perform the operation.
+     * @throws RepositoryException      If another error occurs.
      */
     private NodeImpl getCorrespondingNode(String srcWorkspaceName)
 	    throws NoSuchWorkspaceException, AccessDeniedException,
@@ -2229,7 +2266,7 @@
 	// n1.getPath() = /foo/bar/something[1]
 	// m1.getPath() = /foo
 	//      relpath = bar/something[1]
-	String relPath = getPath().substring(m1.getPath().length()+1);
+	String relPath = getPath().substring(m1.getPath().length() + 1);
 	try {
 	    return (NodeImpl) srcSession.getNodeByUUID(m1.getUUID()).getNode(relPath);
 	} catch (ItemNotFoundException e) {
@@ -2245,7 +2282,7 @@
 	    AccessDeniedException, MergeException, RepositoryException {
 
 	NodeImpl srcNode = doMergeTest(srcWorkspace, bestEffort);
-	if (srcNode!=null) {
+	if (srcNode != null) {
 	    // remove properties
 	    PropertyIterator pi = getProperties();
 	    while (pi.hasNext()) {
@@ -2315,7 +2352,7 @@
 
 	// If N does not have a corresponding node then the merge result for N is leave.
 	NodeImpl srcNode = getCorrespondingNode(srcWorkspace);
-	if (srcNode==null) {
+	if (srcNode == null) {
 	    return null;
 	}
 
@@ -2350,7 +2387,7 @@
 		// add 'offending' version to jcr:mergeFailed property
 		if (hasProperty(ItemImpl.PROPNAME_MERGE_FAILED)) {
 		    Value[] values = getProperty(ItemImpl.PROPNAME_MERGE_FAILED).getValues();
-		    Value[] newValues = new Value[values.length+1];
+		    Value[] newValues = new Value[values.length + 1];
 		    System.arraycopy(values, 0, newValues, 0, values.length);
 		    newValues[values.length] = new ReferenceValue(vp);
 		    setProperty(ItemImpl.PROPNAME_MERGE_FAILED, newValues);
@@ -2554,11 +2591,14 @@
      * Little helper to retrieve the opv value for a property. depends on the
      * implementaion. if nt:frozen is used, need to lookup prop def.
      *
-     * @param prop
+     * @param name
+     * @param type
+     * @param multiValued
      * @return
+     * @throws RepositoryException
      */
-    private int getOPV(PropertyImpl prop) throws RepositoryException {
-	PropertyDefImpl def = getApplicablePropertyDef(prop.getQName(), PropertyType.UNDEFINED);
+    private int guessOPV(QName name, int type, boolean multiValued) throws RepositoryException {
+	PropertyDefImpl def = getApplicablePropertyDef(name, type, multiValued);
 	return def.getOnParentVersion();
     }
 
@@ -2629,7 +2669,16 @@
 		// ignore
 	    } else {
 		// normal property
-		int opv = getOPV(prop);
+		int type = PropertyType.UNDEFINED;
+		if (prop.getDefinition().isMultiple()) {
+		    Value[] values = prop.getValues();
+		    if (values.length != 0) {
+			type = values[0].getType();
+		    }
+		} else {
+		    type = prop.getValue().getType();
+		}
+		int opv = guessOPV(prop.getQName(), type, prop.getDefinition().isMultiple());
 		switch (opv) {
 		    case OnParentVersionAction.ABORT:
 			throw new RepositoryException("Checkin aborted due to OPV in " + prop.safeGetJCRPath());
@@ -2652,7 +2701,7 @@
 	    if (child.isNodeType(NodeTypeRegistry.NT_FROZEN)) {
 		FrozenNode f = (FrozenNode) child;
 		// if frozen node exist, replace
-		// todo: make work for samename siblings
+		// todo: make work for same name siblings
 		if (hasNode(child.getName())) {
 		    remove(child.getName());
 		}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/Test.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/Test.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/Test.java	Mon Sep 20 09:53:38 2004
@@ -99,9 +99,12 @@
 
 	//root.setProperty("blob", new FileInputStream(new File("d:/temp/jckrabbit.zip")));
 
-	//root.setProperty("blah", 1);
-	//root.setProperty("blah", 1.4);
-	//root.setProperty("blah", "blahblah");
+	if (root.hasProperty("blah")) {
+	    root.remove("blah");
+	}
+	root.setProperty("blah", 1);
+	root.setProperty("blah", 1.4);
+	root.setProperty("blah", "blahblah");
 	Node file = root.addNode("blu", "nt:file");
 	file.addNode("jcr:content", "nt:unstructured");
 	root.addNode("blu", "nt:folder");
@@ -123,8 +126,8 @@
 
 	root.save();
 
-	if (root.hasProperty("bla")) {
-	    root.setProperty("bla", (String) null);
+	if (root.hasProperty("blah")) {
+	    root.setProperty("blah", (String) null);
 	}
 	if (!root.hasProperty("blah")) {
 	    String[] strings = new String[]{"huey", "louie", null, "dewey"};
@@ -160,18 +163,17 @@
 
 	Node misc = root.addNode("misc", "nt:unstructured");
 	misc.addMixin("mix:referenceable");
-	Property link = misc.setProperty("link", new Value[]{PathValue.valueOf("../blu[2]")});
+	Property link = misc.setProperty("link", PathValue.valueOf("../blu[2]"));
 	root.save();
-	Node linkTarget = link.getParent().getNode(link.getValues()[0].getString());
+	Node linkTarget = link.getParent().getNode(link.getValue().getString());
 	System.out.println(link.getPath() + " refers to " + linkTarget.getPath());
 
-	root.setProperty("ref", new Value[]{new ReferenceValue(misc)});
+	root.setProperty("ref", new ReferenceValue(misc));
 	root.save();
 	PropertyIterator pi = misc.getReferences();
 	while (pi.hasNext()) {
 	    Property prop = pi.nextProperty();
-	    String uuid = prop.getValues()[0].getString();
-	    Node target = session.getNodeByUUID(uuid);
+	    Node target = prop.getNode();
 	    System.out.println(prop.getPath() + " is a reference to " + target.getPath());
 	}
 /*
@@ -184,9 +186,9 @@
 	}
 */
 	String date1 = "2003-07-28T06:18:57.848+01:00";
-	root.setProperty("date", new Value[]{new DateValue(new StringValue(date1).getDate())});
+	root.setProperty("date", new DateValue(new StringValue(date1).getDate()));
 	Property p = root.getProperty("date");
-	Value val = p.getValues()[0];
+	Value val = p.getValue();
 	Calendar d = val.getDate();
 
 	Node imported = null;
@@ -196,7 +198,7 @@
 	    imported = root.getNode("imported");
 	}
 
-	importNode(new File("d:/dev/jackrabbit/src/java"), imported);
+	importNode(new File("d:/dev/jsr170/jackrabbit/src/java"), imported);
 
 	if (root.hasNode("foo")) {
 	    root.remove("foo");
@@ -205,8 +207,8 @@
 	Node n = root.addNode("foo", "nt:folder");
 	Node n2 = n.addNode("foofile", "nt:file");
 	Node n3 = n2.addNode("jcr:content", "nt:unstructured");
-	Property prop1 = n3.setProperty("prop1", new Value[]{new LongValue(123)});
-	Property prop2 = n3.setProperty("prop2", new Value[]{new StringValue("blahblah")});
+	Property prop1 = n3.setProperty("prop1", new LongValue(123));
+	Property prop2 = n3.setProperty("prop2", new StringValue("blahblah"));
 
 	System.out.println("before save()...");
 	System.out.println();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/EffectiveNodeType.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/EffectiveNodeType.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/EffectiveNodeType.java	Mon Sep 20 09:53:38 2004
@@ -455,11 +455,12 @@
      *
      * @param name
      * @param type
+     * @param multiValued
      * @return
      * @throws ConstraintViolationException if no applicable property definition
      *                                      could be found
      */
-    public PropDef getApplicablePropertyDef(QName name, int type)
+    public PropDef getApplicablePropertyDef(QName name, int type, boolean multiValued)
 	    throws ConstraintViolationException {
 	ChildItemDef def = (ChildItemDef) namedItemDefs.get(name);
 	if (def == null) {
@@ -473,8 +474,11 @@
 		if (reqType == PropertyType.UNDEFINED ||
 			type == PropertyType.UNDEFINED ||
 			reqType == type) {
-		    // found match
-		    return pd;
+		    // match multiValued flag
+		    if (multiValued == pd.isMultiple()) {
+			// found match
+			return pd;
+		    }
 		}
 	    }
 	} else {
@@ -486,8 +490,11 @@
 		if (reqType == PropertyType.UNDEFINED ||
 			type == PropertyType.UNDEFINED ||
 			reqType == type) {
-		    // found match
-		    return pd;
+		    // match multiValued flag
+		    if (multiValued == pd.isMultiple()) {
+			// found match
+			return pd;
+		    }
 		}
 	    }
 	}
@@ -617,13 +624,28 @@
 		ChildItemDef existing = (ChildItemDef) iter.next();
 		// compare with existing definition
 		if (def.definesNode() == existing.definesNode()) {
-		    // conflict
-		    String msg = "An item definition in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existing.getDeclaringNodeType() + "': ambiguos residual item definition";
-		    log.error(msg);
-		    throw new NodeTypeConflictException(msg);
+		    if (!def.definesNode()) {
+			// property definition
+			PropDef pd = (PropDef) def;
+			PropDef epd = (PropDef) existing;
+			// compare type & multiValued flag
+			if (pd.getRequiredType() == epd.getRequiredType() &&
+				pd.isMultiple() == epd.isMultiple()) {
+			    // conflict
+			    String msg = "A property definition in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existing.getDeclaringNodeType() + "': ambiguos residual property definition";
+			    log.error(msg);
+			    throw new NodeTypeConflictException(msg);
+			}
+		    } else {
+			// child node definition
+			// conflict
+			String msg = "A child node definition in node type '" + def.getDeclaringNodeType() + "' conflicts with node type '" + existing.getDeclaringNodeType() + "': ambiguos residual child node definition";
+			log.error(msg);
+			throw new NodeTypeConflictException(msg);
+		    }
 		}
 	    }
-	    // @todo check for ambiguous definitions & other conflicts
+	    // @todo do further checks for ambiguous definitions & other conflicts
 	    unnamedItemDefs.add(def);
 	}
 	// @todo implement further validations

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeDefId.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeDefId.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeDefId.java	Mon Sep 20 09:53:38 2004
@@ -37,6 +37,7 @@
 	if (def == null) {
 	    throw new IllegalArgumentException("ChildNodeDef argument can not be null");
 	}
+	// build key (format: <declaringNodeType>/<name>/<defaultPrimaryType>/<requiredPrimaryTypes>)
 	StringBuffer sb = new StringBuffer();
 
 	sb.append(def.getDeclaringNodeType().toString());

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeTypeImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeTypeImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/NodeTypeImpl.java	Mon Sep 20 09:53:38 2004
@@ -104,13 +104,15 @@
      *
      * @param propertyName
      * @param type
+     * @param multiValued
      * @return
      * @throws RepositoryException if no applicable property definition
      *                             could be found
      */
-    public PropertyDefImpl getApplicablePropertyDef(QName propertyName, int type)
+    public PropertyDefImpl getApplicablePropertyDef(QName propertyName, int type,
+						    boolean multiValued)
 	    throws RepositoryException {
-	return new PropertyDefImpl(ent.getApplicablePropertyDef(propertyName, type),
+	return new PropertyDefImpl(ent.getApplicablePropertyDef(propertyName, type, multiValued),
 		ntMgr, nsResolver);
     }
 
@@ -378,14 +380,59 @@
      * @see NodeType#canSetProperty(String, Value)
      */
     public boolean canSetProperty(String propertyName, Value value) {
+	if (value == null) {
+	    // setting a property to null is equivalent of removing it
+	    return canRemoveItem(propertyName);
+	}
 	try {
 	    QName name = QName.fromJCRName(propertyName, nsResolver);
-	    PropertyDefImpl def = getApplicablePropertyDef(name, value == null ? PropertyType.UNDEFINED : value.getType());
+	    int type = (value == null) ? PropertyType.UNDEFINED : value.getType();
+	    PropertyDefImpl def = getApplicablePropertyDef(name, type, false);
 	    if (def.isProtected()) {
 		return false;
 	    }
+	    if (def.isMultiple()) {
+		return false;
+	    }
 	    InternalValue internalValue = InternalValue.create(value, nsResolver);
 	    checkSetPropertyValueConstraints(def, new InternalValue[]{internalValue});
+	    return true;
+	} catch (BaseException be) {
+	    // implementation specific exception, fall through
+	} catch (RepositoryException re) {
+	    // fall through
+	}
+	return false;
+    }
+
+    /**
+     * @see NodeType#canSetProperty(String, Value[])
+     */
+    public boolean canSetProperty(String propertyName, Value values[]) {
+	if (values == null) {
+	    // setting a property to null is equivalent of removing it
+	    return canRemoveItem(propertyName);
+	}
+	try {
+	    QName name = QName.fromJCRName(propertyName, nsResolver);
+	    int type = (values == null || values.length == 0) ? PropertyType.UNDEFINED : values[0].getType();
+	    PropertyDefImpl def = getApplicablePropertyDef(name, type, true);
+	    if (def.isProtected()) {
+		return false;
+	    }
+	    if (!def.isMultiple()) {
+		return false;
+	    }
+	    ArrayList list = new ArrayList();
+	    // convert values and compact array (purge null entries)
+	    for (int i = 0; i < values.length; i++) {
+		if (values[i] != null) {
+		    InternalValue internalValue = InternalValue.create(values[i], nsResolver);
+		    list.add(internalValue);
+		}
+	    }
+	    InternalValue[] internalValues = (InternalValue[]) list.toArray(new InternalValue[list.size()]);
+	    checkSetPropertyValueConstraints(def, internalValues);
 	    return true;
 	} catch (BaseException be) {
 	    // implementation specific exception, fall through

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/PropDefId.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/PropDefId.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/PropDefId.java	Mon Sep 20 09:53:38 2004
@@ -34,7 +34,7 @@
 	if (def == null) {
 	    throw new IllegalArgumentException("PropDef argument can not be null");
 	}
-	// build key (format: <declaring node type>/<name>/<type>/<multiple flag>)
+	// build key (format: <declaringNodeType>/<name>/<requiredType>/<multiple>)
 	StringBuffer sb = new StringBuffer();
 
 	sb.append(def.getDeclaringNodeType().toString());

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/builtin_nodetypes.xml
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/builtin_nodetypes.xml	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/nodetype/builtin_nodetypes.xml	Mon Sep 20 09:53:38 2004
@@ -64,6 +64,7 @@
     <nodeType name="nt:unstructured" mixin="false" orderableChildNodes="true" supertypes="nt:base">
 	<childNodeDef name="" requiredPrimaryTypes="nt:base" defaultPrimaryType="nt:unstructured" autoCreate="false" mandatory="false" onParentVersion="COPY" protected="false" primaryItem="false" sameNameSibs="true"/>
 	<propertyDef name="" type="undefined" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="COPY" protected="false" primaryItem="false" multiple="true"/>
+	<propertyDef name="" type="undefined" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="COPY" protected="false" primaryItem="false" multiple="false"/>
     </nodeType>
     <nodeType name="nt:hierarchyNode" mixin="false" orderableChildNodes="false" supertypes="nt:base">
 	<propertyDef name="jcr:created" type="Date" valueConstraint="" defaultValues="" autoCreate="true" mandatory="true" onParentVersion="INITIALIZE" protected="true" primaryItem="false" multiple="false"/>
@@ -124,6 +125,7 @@
 	<propertyDef name="jcr:frozenMixinTypes" type="Name" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="ABORT" protected="true" primaryItem="false" multiple="true"/>
 	<propertyDef name="jcr:frozenUUID" type="String" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="ABORT" protected="true" primaryItem="false" multiple="false"/>
 	<propertyDef name="" type="undefined" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="ABORT" protected="true" primaryItem="false" multiple="true"/>
+	<propertyDef name="" type="undefined" valueConstraint="" defaultValues="" autoCreate="false" mandatory="false" onParentVersion="ABORT" protected="true" primaryItem="false" multiple="false"/>
 	<childNodeDef name="" requiredPrimaryTypes="nt:base" defaultPrimaryType="nt:frozen" autoCreate="false" mandatory="false" onParentVersion="ABORT" protected="true" primaryItem="false" sameNameSibs="true"/>
     </nodeType>
     <nodeType name="nt:version" mixin="false" orderableChildNodes="false" supertypes="nt:frozen,mix:referenceable">

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/DocViewImportHandler.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/DocViewImportHandler.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/DocViewImportHandler.java	Mon Sep 20 09:53:38 2004
@@ -15,9 +15,9 @@
  */
 package org.apache.jackrabbit.jcr.core.xml;
 
-import org.apache.log4j.Logger;
 import org.apache.jackrabbit.jcr.core.*;
 import org.apache.jackrabbit.jcr.core.nodetype.NodeTypeRegistry;
+import org.apache.log4j.Logger;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -25,7 +25,6 @@
 
 import javax.jcr.RepositoryException;
 import javax.jcr.StringValue;
-import javax.jcr.Value;
 import java.util.Stack;
 
 /**
@@ -87,7 +86,7 @@
 		    }
 		}
 		StringValue val = new StringValue(atts.getValue(i));
-		currentParent.setProperty(propName, new Value[]{val});
+		currentParent.setProperty(propName, val);
 	    }
 	} catch (RepositoryException re) {
 	    throw new SAXException(re);
@@ -105,8 +104,7 @@
 	    NodeImpl currentParent = (NodeImpl) parents.peek();
 	    NodeImpl txtNode = (NodeImpl) currentParent.addNode(DocViewSAXEventGenerator.NODENAME_XMLTEXT);
 	    StringValue val = new StringValue(new String(ch, start, length));
-	    txtNode.setProperty(DocViewSAXEventGenerator.PROPNAME_XMLCHARACTERS,
-		    new Value[]{val});
+	    txtNode.setProperty(DocViewSAXEventGenerator.PROPNAME_XMLCHARACTERS, val);
 	} catch (RepositoryException re) {
 	    throw new SAXException(re);
 	}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/SysViewImportHandler.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/SysViewImportHandler.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/jcr/core/xml/SysViewImportHandler.java	Mon Sep 20 09:53:38 2004
@@ -28,6 +28,7 @@
 import javax.jcr.*;
 import javax.jcr.nodetype.NodeDef;
 import javax.jcr.nodetype.PropertyDef;
+import javax.jcr.nodetype.ConstraintViolationException;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -272,10 +273,25 @@
 		    }
 		    if (current.node.hasProperty(currentPropName)) {
 			PropertyDef def = current.node.getProperty(currentPropName).getDefinition();
-			if (!def.isProtected()) {
+			if (def.isProtected()) {
+			    // ignore protected property
+			    // reset temp fields and get outta here
+			    currentPropValues = null;
+			    return;
+			}
+		    }
+		    // multi- or single-valued property?
+		    if (vals.length == 1) {
+			// could be single- or multi-valued (n == 1)
+			try {
+			    // try setting single-value
+			    current.node.setProperty(currentPropName, vals[0]);
+			} catch (ConstraintViolationException cve) {
+			    // try setting value array
 			    current.node.setProperty(currentPropName, vals);
 			}
 		    } else {
+			// can only be multi-valued (n == 0 || n > 1)
 			current.node.setProperty(currentPropName, vals);
 		    }
 		}