You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2013/07/01 14:07:55 UTC
svn commit: r1498401 - in /jackrabbit/oak/trunk:
oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/
oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/
oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/
Author: jukka
Date: Mon Jul 1 12:07:54 2013
New Revision: 1498401
URL: http://svn.apache.org/r1498401
Log:
OAK-702: Optimize access to node type information
Optimize property definition access in setProperty()
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java?rev=1498401&r1=1498400&r2=1498401&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/PropertyStates.java Mon Jul 1 12:07:54 2013
@@ -289,4 +289,14 @@ public final class PropertyStates {
}
}
+ public static PropertyState convert(PropertyState state, Type<?> type) {
+ if (type == state.getType()
+ || (type == Type.UNDEFINED && !state.isArray())
+ || (type == Type.UNDEFINEDS && state.isArray())) {
+ return state;
+ } else {
+ return createProperty(state.getName(), state.getValue(type), type);
+ }
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java?rev=1498401&r1=1498400&r2=1498401&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java Mon Jul 1 12:07:54 2013
@@ -16,7 +16,17 @@
*/
package org.apache.jackrabbit.oak.jcr;
-import java.util.Arrays;
+import static com.google.common.collect.Lists.newArrayListWithCapacity;
+import static org.apache.jackrabbit.oak.api.Type.NAME;
+import static org.apache.jackrabbit.oak.api.Type.NAMES;
+import static org.apache.jackrabbit.oak.api.Type.PATH;
+import static org.apache.jackrabbit.oak.api.Type.PATHS;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.UNDEFINED;
+import static org.apache.jackrabbit.oak.api.Type.UNDEFINEDS;
+import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+
+import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -25,16 +35,13 @@ import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
-import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
-import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.ItemDefinition;
import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.oak.api.PropertyState;
@@ -45,10 +52,8 @@ import org.apache.jackrabbit.oak.jcr.del
import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
import org.apache.jackrabbit.oak.jcr.delegate.SessionOperation;
import org.apache.jackrabbit.oak.plugins.memory.MemoryPropertyBuilder;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.plugins.nodetype.DefinitionProvider;
import org.apache.jackrabbit.oak.plugins.nodetype.EffectiveNodeTypeProvider;
-import org.apache.jackrabbit.value.ValueHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -336,110 +341,57 @@ abstract class ItemImpl<T extends ItemDe
return sessionContext.getVersionManager();
}
- /**
- * Determine property type from property definition and default to
- * {@code defaultType} in the case of {@link javax.jcr.PropertyType#UNDEFINED}.
- *
- * @param definition
- * @param defaultType
- * @return the type determined from the property definition
- */
- private static int getType(PropertyDefinition definition, int defaultType) {
- if (definition.getRequiredType() == PropertyType.UNDEFINED) {
- return defaultType == PropertyType.UNDEFINED ? PropertyType.STRING : defaultType;
- } else {
- return definition.getRequiredType();
- }
- }
-
- /**
- * Removes all {@code null} values from the given array.
- *
- * @param values value array
- * @return value array without {@code null} entries
- */
- private static Value[] compact(Value[] values) {
- int n = 0;
- for (Value value : values) {
- if (value != null) {
- n++;
- }
+ protected PropertyState createSingleState(
+ String oakName, Value value, Type<?> type)
+ throws RepositoryException {
+ if (type == UNDEFINED) {
+ type = Type.fromTag(value.getType(), false);
}
- if (n == values.length) {
- return values;
+ if (type == NAME || type == PATH) {
+ return createProperty(oakName, getOakValue(value, type), type);
} else {
- Value[] nonNullValues = new Value[n];
- for (int i = 0, j = 0; i < values.length; i++) {
- if (values[i] != null) {
- nonNullValues[j++] = values[i];
- }
- }
- return nonNullValues;
+ return createProperty(oakName, value);
}
}
- /**
- * Create a single value property state given a {@code name}, a {@code value} and a
- * property {@code definition}. If the value does not match the property definition
- * it is converted accordingly
- *
- * @param name
- * @param value
- * @param definition
- * @return single valued property state
- * @throws ValueFormatException if {@code definition} defines a multi valued property
- * @throws RepositoryException if value conversion fails
- */
- PropertyState createSingleState(String name, Value value, PropertyDefinition definition)
+ protected PropertyState createMultiState(
+ String oakName, List<Value> values, Type<?> type)
throws RepositoryException {
- if (definition.isMultiple()) {
- throw new ValueFormatException("Cannot set single value to multivalued property");
+ if (values.isEmpty()) {
+ Type<?> base = type.getBaseType();
+ if (base == UNDEFINED) {
+ base = STRING;
+ }
+ return MemoryPropertyBuilder.array(base)
+ .setName(oakName).getPropertyState();
}
-
- int targetType = getType(definition, value.getType());
- if (targetType == value.getType()) {
- return PropertyStates.createProperty(name, value);
+ if (type == UNDEFINEDS) {
+ type = Type.fromTag(values.get(0).getType(), true);
}
- else {
- return PropertyStates.createProperty(name, ValueHelper.convert(
- value, targetType, getValueFactory()));
+ if (type == NAMES || type == PATHS) {
+ Type<?> base = type.getBaseType();
+ List<String> strings = newArrayListWithCapacity(values.size());
+ for (Value value : values) {
+ strings.add(getOakValue(value, base));
+ }
+ return createProperty(oakName, strings, type);
+ } else {
+ return createProperty(oakName, values, type.tag());
}
}
- /**
- * Create a single value property state given a {@code name}, a {@code type}, a
- * {@code value} and a property {@code definition}. If the type does not match the
- * property definition it is converted accordingly
- *
- * @param name
- * @param type
- * @param values
- * @param definition
- * @return array valued property state
- * @throws ValueFormatException if {@code definition} does not define a multi valued property
- * @throws RepositoryException if value conversion fails
- */
- PropertyState createMultiState(String name, int type, Value[] values, PropertyDefinition definition)
+ private String getOakValue(Value value, Type<?> type)
throws RepositoryException {
- if (!definition.isMultiple()) {
- throw new ValueFormatException("Cannot set value array to single value property");
- }
-
- Value[] nonNullValues = compact(values);
- int targetType = getType(definition, type);
- if (nonNullValues.length == 0) {
- if (targetType == PropertyType.UNDEFINED) {
- // default to string when no other type hints are available
- targetType = PropertyType.STRING;
+ if (type == NAME) {
+ return getOakName(value.getString());
+ } else if (type == PATH) {
+ String path = value.getString();
+ if (!path.startsWith("[")) { // leave identifiers unmodified
+ path = getOakPathOrThrow(path);
}
- return MemoryPropertyBuilder
- .array(Type.fromTag(targetType, false), name)
- .getPropertyState();
- } else if (targetType == type) {
- return PropertyStates.createProperty(name, Arrays.asList(nonNullValues));
+ return path;
} else {
- return PropertyStates.createProperty(name, Arrays.asList(ValueHelper.convert(
- values, targetType, getValueFactory())));
+ throw new IllegalArgumentException();
}
}
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java?rev=1498401&r1=1498400&r2=1498401&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java Mon Jul 1 12:07:54 2013
@@ -20,6 +20,7 @@ import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.jcr.AccessDeniedException;
@@ -46,7 +47,6 @@ import javax.jcr.nodetype.NoSuchNodeType
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
@@ -56,6 +56,8 @@ import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.ItemNameMatcher;
import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
@@ -83,7 +85,6 @@ import static com.google.common.base.Pre
import static java.util.Collections.singleton;
import static javax.jcr.Property.JCR_LOCK_IS_DEEP;
import static javax.jcr.Property.JCR_LOCK_OWNER;
-import static javax.jcr.PropertyType.UNDEFINED;
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
@@ -313,10 +314,11 @@ public class NodeImpl<T extends NodeDele
if (value != null) {
boolean exactTypeMatch = true;
if (type == PropertyType.UNDEFINED) {
- type = PropertyType.STRING;
+ type = value.getType();
exactTypeMatch = false;
+ } else {
+ value = ValueHelper.convert(value, type, getValueFactory());
}
- value = ValueHelper.convert(value, type, getValueFactory());
return internalSetProperty(name, value, exactTypeMatch);
} else {
return internalRemoveProperty(name);
@@ -327,6 +329,7 @@ public class NodeImpl<T extends NodeDele
public Property setProperty(String name, Value[] values)
throws RepositoryException {
if (values != null) {
+ // TODO: type
return internalSetProperty(name, values, ValueHelper.getType(values), false);
} else {
return internalRemoveProperty(name);
@@ -336,11 +339,14 @@ public class NodeImpl<T extends NodeDele
@Override @Nonnull
public Property setProperty(String jcrName, Value[] values, int type)
throws RepositoryException {
- if (type == UNDEFINED) {
- return setProperty(jcrName, values);
- } else if (values != null) {
+ if (values != null) {
+ boolean exactTypeMatch = true;
+ if (type == PropertyType.UNDEFINED) {
+ type = PropertyType.STRING;
+ exactTypeMatch = false;
+ }
values = ValueHelper.convert(values, type, getValueFactory());
- return internalSetProperty(jcrName, values, type, true);
+ return internalSetProperty(jcrName, values, type, exactTypeMatch);
} else {
return internalRemoveProperty(jcrName);
}
@@ -350,9 +356,9 @@ public class NodeImpl<T extends NodeDele
public Property setProperty(String name, String[] values)
throws RepositoryException {
if (values != null) {
- Value[] vs = ValueHelper.convert(
- values, PropertyType.STRING, getValueFactory());
- return internalSetProperty(name, vs, UNDEFINED, false);
+ int type = PropertyType.STRING;
+ Value[] vs = ValueHelper.convert(values, type, getValueFactory());
+ return internalSetProperty(name, vs, type, false);
} else {
return internalRemoveProperty(name);
}
@@ -361,11 +367,14 @@ public class NodeImpl<T extends NodeDele
@Override @Nonnull
public Property setProperty(String name, String[] values, int type)
throws RepositoryException {
- if (type == UNDEFINED) {
- return setProperty(name, values);
- } else if (values != null) {
+ if (values != null) {
+ boolean exactTypeMatch = true;
+ if (type == PropertyType.UNDEFINED) {
+ type = PropertyType.STRING;
+ exactTypeMatch = false;
+ }
Value[] vs = ValueHelper.convert(values, type, getValueFactory());
- return internalSetProperty(name, vs, type, true);
+ return internalSetProperty(name, vs, type, exactTypeMatch);
} else {
return internalRemoveProperty(name);
}
@@ -1332,10 +1341,9 @@ public class NodeImpl<T extends NodeDele
}
// TODO: END
- String jcrPrimaryType = getOakPathOrThrow(Property.JCR_PRIMARY_TYPE);
- Value value = getValueFactory().createValue(nodeTypeName, PropertyType.NAME);
-
- dlg.setProperty(PropertyStates.createProperty(jcrPrimaryType, value));
+ PropertyState state = PropertyStates.createProperty(
+ JCR_PRIMARYTYPE, getOakName(nodeTypeName), NAME);
+ dlg.setProperty(state, true, true);
dlg.setOrderableChildren(nt.hasOrderableChildNodes());
}
@@ -1343,25 +1351,22 @@ public class NodeImpl<T extends NodeDele
String jcrName, final Value value, final boolean exactTypeMatch)
throws RepositoryException {
final String oakName = getOakPathOrThrow(checkNotNull(jcrName));
- checkNotNull(value);
+ final PropertyState state = createSingleState(
+ oakName, value, Type.fromTag(value.getType(), false));
return perform(new ItemWriteOperation<Property>() {
@Override
protected void checkPreconditions() throws RepositoryException {
super.checkPreconditions();
if (!isCheckedOut()) {
- throw new VersionException("Cannot set property. Node is checked in.");
+ throw new VersionException(
+ "Cannot set property. Node is checked in.");
}
}
-
@Override
public Property perform() throws RepositoryException {
- // TODO: Avoid extra JCR method calls (OAK-672)
- PropertyDefinition definition = getEffectiveNodeType()
- .getPropertyDefinition(oakName, false, value.getType(), exactTypeMatch);
-
- checkProtected(definition);
- PropertyState state = createSingleState(oakName, value, definition);
- return new PropertyImpl(dlg.setProperty(state), sessionContext);
+ return new PropertyImpl(
+ dlg.setProperty(state, exactTypeMatch, false),
+ sessionContext);
}
});
}
@@ -1371,21 +1376,43 @@ public class NodeImpl<T extends NodeDele
final int type, final boolean exactTypeMatch)
throws RepositoryException {
final String oakName = getOakPathOrThrow(checkNotNull(jcrName));
- checkNotNull(values);
+ final PropertyState state = createMultiState(
+ oakName, compact(values), Type.fromTag(type, true));
return perform(new ItemWriteOperation<Property>() {
@Override
+ protected void checkPreconditions() throws RepositoryException {
+ super.checkPreconditions();
+ if (!isCheckedOut()) {
+ throw new VersionException(
+ "Cannot set property. Node is checked in.");
+ }
+ }
+ @Override
public Property perform() throws RepositoryException {
- // TODO: Avoid extra JCR method calls (OAK-672)
- PropertyDefinition definition = getEffectiveNodeType()
- .getPropertyDefinition(oakName, true, type, exactTypeMatch);
-
- checkProtected(definition);
- PropertyState state = createMultiState(oakName, type, values, definition);
- return new PropertyImpl(dlg.setProperty(state), sessionContext);
+ return new PropertyImpl(
+ dlg.setProperty(state, exactTypeMatch, false),
+ sessionContext);
}
});
}
+ /**
+ * Removes all {@code null} values from the given array.
+ *
+ * @param values value array
+ * @return value list without {@code null} entries
+ */
+ private static List<Value> compact(Value[] values) {
+ List<Value> list = Lists.newArrayListWithCapacity(values.length);
+ for (Value value : values) {
+ if (value != null) {
+ list.add(value);
+ }
+ }
+ return list;
+ }
+
+
private Property internalRemoveProperty(final String jcrName)
throws RepositoryException {
final String oakName = getOakName(checkNotNull(jcrName));
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java?rev=1498401&r1=1498400&r2=1498401&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java Mon Jul 1 12:07:54 2013
@@ -17,11 +17,6 @@
package org.apache.jackrabbit.oak.jcr;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
-import static org.apache.jackrabbit.oak.api.Type.NAME;
-import static org.apache.jackrabbit.oak.api.Type.NAMES;
-import static org.apache.jackrabbit.oak.api.Type.PATH;
-import static org.apache.jackrabbit.oak.api.Type.PATHS;
-import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
import java.io.InputStream;
import java.math.BigDecimal;
@@ -43,7 +38,6 @@ import javax.jcr.ValueFactory;
import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.PropertyDefinition;
-import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree.Status;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.jcr.delegate.NodeDelegate;
@@ -444,19 +438,7 @@ public class PropertyImpl extends ItemIm
Value converted = ValueHelper.convert(
value, type.tag(), getValueFactory());
-
- PropertyState state;
- if (type == NAME) {
- String name = getOakName(converted.getString());
- state = createProperty(dlg.getName(), name, NAME);
- } else if (type == PATH) {
- String path = getOakPathOrThrow(converted.getString());
- state = createProperty(dlg.getName(), path, PATH);
- } else {
- state = createProperty(dlg.getName(), converted);
- }
-
- dlg.setState(state);
+ dlg.setState(createSingleState(dlg.getName(), converted, type));
return null;
}
});
@@ -481,27 +463,7 @@ public class PropertyImpl extends ItemIm
values[i], type.tag(), factory));
}
}
-
- PropertyState state;
- if (type == NAMES) {
- List<String> names =
- newArrayListWithCapacity(converted.size());
- for (Value name : converted) {
- names.add(getOakName(name.getString()));
- }
- state = createProperty(dlg.getName(), names, NAMES);
- } else if (type == PATHS) {
- List<String> paths =
- newArrayListWithCapacity(converted.size());
- for (Value path : converted) {
- paths.add(getOakPathOrThrow(path.getString()));
- }
- state = createProperty(dlg.getName(), paths, PATHS);
- } else {
- state = createProperty(dlg.getName(), converted, type.tag());
- }
-
- dlg.setState(state);
+ dlg.setState(createMultiState(dlg.getName(), converted, type));
return null;
}
});
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java?rev=1498401&r1=1498400&r2=1498401&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/NodeDelegate.java Mon Jul 1 12:07:54 2013
@@ -36,13 +36,17 @@ import static org.apache.jackrabbit.JcrC
import static org.apache.jackrabbit.JcrConstants.JCR_NODETYPENAME;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.JCR_PROTECTED;
+import static org.apache.jackrabbit.JcrConstants.JCR_REQUIREDTYPE;
import static org.apache.jackrabbit.JcrConstants.JCR_SAMENAMESIBLINGS;
import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
import static org.apache.jackrabbit.oak.api.Type.DATE;
import static org.apache.jackrabbit.oak.api.Type.NAME;
import static org.apache.jackrabbit.oak.api.Type.NAMES;
import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.UNDEFINED;
+import static org.apache.jackrabbit.oak.api.Type.UNDEFINEDS;
import static org.apache.jackrabbit.oak.commons.PathUtils.dropIndexFromName;
import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_CREATEDBY;
import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_IS_ABSTRACT;
@@ -419,18 +423,120 @@ public class NodeDelegate extends ItemDe
* @return the set property
*/
@Nonnull
- public PropertyDelegate setProperty(PropertyState propertyState) throws RepositoryException {
+ public PropertyDelegate setProperty(
+ PropertyState propertyState, boolean exactTypeMatch,
+ boolean setProtected) throws RepositoryException {
Tree tree = getTree();
- String propName = propertyState.getName();
- PropertyState old = tree.getProperty(propName);
+ String name = propertyState.getName();
+ Type<?> type = propertyState.getType();
+
+ PropertyState old = tree.getProperty(name);
if (old != null && old.isArray() && !propertyState.isArray()) {
- throw new ValueFormatException("Attempt to assign a single value to multi-valued property.");
+ throw new ValueFormatException(
+ "Can not assign a single value to multi-valued property: "
+ + propertyState);
}
if (old != null && !old.isArray() && propertyState.isArray()) {
- throw new ValueFormatException("Attempt to assign multiple values to single valued property.");
+ throw new ValueFormatException(
+ "Can not assign multiple values to single valued property: "
+ + propertyState);
+ }
+
+ Tree definition = findMatchingPropertyDefinition(
+ tree, name, type, exactTypeMatch);
+ if (definition == null) {
+ throw new ConstraintViolationException(
+ "No matching property definition: " + propertyState);
+ } else if (!setProtected && getBoolean(definition, JCR_PROTECTED)) {
+ throw new ConstraintViolationException(
+ "Property is protected: " + propertyState);
}
+ Type<?> requiredType =
+ Type.fromString(getString(definition, JCR_REQUIREDTYPE));
+ if (requiredType != Type.UNDEFINED) {
+ if (getBoolean(definition, JCR_MULTIPLE)) {
+ requiredType = requiredType.getArrayType();
+ }
+ propertyState = PropertyStates.convert(propertyState, requiredType);
+ }
+
tree.setProperty(propertyState);
- return new PropertyDelegate(sessionDelegate, tree, propName);
+ return new PropertyDelegate(sessionDelegate, tree, name);
+ }
+
+ private Tree findMatchingPropertyDefinition(
+ Tree tree, String propertyName, Type<?> propertyType,
+ boolean exactTypeMatch) {
+ Tree typeRoot = sessionDelegate.getRoot().getTree(NODE_TYPES_PATH);
+
+ // Find applicable node types
+ List<Tree> types = newArrayList();
+ String primaryName = getName(tree, JCR_PRIMARYTYPE);
+ if (primaryName == null) {
+ primaryName = NT_BASE;
+ }
+ types.add(typeRoot.getChild(primaryName));
+ for (String mixinName : getNames(tree, JCR_MIXINTYPES)) {
+ types.add(typeRoot.getChild(mixinName));
+ }
+
+ // Escape the property name for looking up a matching definition
+ String escapedName;
+ if (JCR_PRIMARYTYPE.equals(propertyName)) {
+ escapedName = "oak:primaryType";
+ } else if (JCR_MIXINTYPES.equals(propertyName)) {
+ escapedName = "oak:mixinTypes";
+ } else if (JCR_UUID.equals(propertyName)) {
+ escapedName = "oak:uuid";
+ } else {
+ escapedName = propertyName;
+ }
+
+ String definedType = propertyType.toString();
+ String undefinedType = UNDEFINED.toString();
+ if (propertyType.isArray()) {
+ undefinedType = UNDEFINEDS.toString();
+ }
+
+ // First look for a matching named property definition
+ for (Tree type : types) {
+ Tree definitions = type
+ .getChild(OAK_NAMED_PROPERTY_DEFINITIONS)
+ .getChild(escapedName);
+ Tree definition = definitions.getChild(definedType);
+ if (definition.exists()) {
+ return definition;
+ }
+ definition = definitions.getChild(undefinedType);
+ if (definition.exists()) {
+ return definition;
+ }
+ if (!exactTypeMatch) {
+ for (Tree def : definitions.getChildren()) {
+ return def;
+ }
+ }
+ }
+
+ // Then look through any residual property definitions
+ for (Tree type : types) {
+ Tree definitions = type.getChild(OAK_RESIDUAL_PROPERTY_DEFINITIONS);
+ Tree definition = definitions.getChild(definedType);
+ if (definition.exists()) {
+ return definition;
+ }
+ definition = definitions.getChild(undefinedType);
+ if (definition.exists()) {
+ return definition;
+ }
+ if (!exactTypeMatch) {
+ for (Tree def : definitions.getChildren()) {
+ return def;
+ }
+ }
+ }
+
+ return null;
}
/**
@@ -707,6 +813,16 @@ public class NodeDelegate extends ItemDe
}
@CheckForNull
+ private static String getString(Tree tree, String name) {
+ PropertyState property = tree.getProperty(name);
+ if (property != null && property.getType() == STRING) {
+ return property.getValue(STRING);
+ } else {
+ return null;
+ }
+ }
+
+ @CheckForNull
private static String getName(Tree tree, String name) {
PropertyState property = tree.getProperty(name);
if (property != null && property.getType() == NAME) {