DNA SVN: r785 - in trunk/dna-jcr/src: test/java/org/jboss/dna/jcr and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-20 14:40:19 -0400 (Fri, 20 Mar 2009)
New Revision: 785
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Log:
DNA-302 Make JcrNodeTypeManager not dependent on JcrSession
Applied the patch, which splits JcrNodeTypeManager into repository-scoped functionality (RepositoryNodeTypeManager) and session-scoped functionality (JcrNodeTypeManager) with the key difference being that the session-scoped node type manager is able to translate names based on transient namespace remappings.
The RepositoryNodeTypeManager is the maintainer of node type information for the entire repository at run-time. The
repository manager maintains a list of all node types and the ability to retrieve node types by Name.
The JCR 1.0 and 2.0 specifications both require that node type information be shared across all sessions within a
repository and that the javax.jcr.NodeTypeManager perform operations based on the string versions of Names based
on the permanent (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by
maintaining a single master repository of all node type information (the RepositoryNodeTypeManager) and per-session
wrappers (JcrNodeTypeManager) for this master repository that perform String to Name translation based on the
javax.jcr.Session's transient mappings and then delegating node type lookups to the repository manager.
Also removed JcrSession references from all the type-related objects and replaced them with ExecutionContexts.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -100,14 +100,11 @@
*/
public final String getUUID() throws RepositoryException {
// Return "jcr:uuid" only if node is referenceable
- Property mixinsProp = getProperty(JcrLexicon.MIXIN_TYPES);
- if (mixinsProp != null) {
- String referenceableMixinName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
- for (Value value : mixinsProp.getValues()) {
- if (referenceableMixinName.equals(value.getString())) return nodeUuid.toString();
- }
+ String referenceableTypeName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
+ if (!isNodeType(referenceableTypeName)) {
+ throw new UnsupportedRepositoryOperationException();
}
- throw new UnsupportedRepositoryOperationException();
+ return nodeUuid.toString();
}
/**
@@ -171,7 +168,7 @@
* @see javax.jcr.Node#getMixinNodeTypes()
*/
public NodeType[] getMixinNodeTypes() throws RepositoryException {
- NodeTypeManager nodeTypeManager = session().getWorkspace().getNodeTypeManager();
+ NodeTypeManager nodeTypeManager = session().nodeTypeManager();
Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -57,6 +57,9 @@
protected static final boolean ORDERABLE_CHILD_NODES = true;
protected static final boolean UNORDERABLE_CHILD_NODES = false;
+ /** Link to the repository node type manager to which the nodes defined in this source belong. */
+ protected static final RepositoryNodeTypeManager NO_NODE_TYPE_MANAGER = null;
+
/** The predecessor node type source. */
private final JcrNodeTypeSource predecessor;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -78,7 +78,7 @@
}
JcrValue createValue( Object value ) throws RepositoryException {
- return new JcrValue(context().getValueFactories(), propertyInfo().getPropertyType(), value);
+ return new JcrValue(context().getValueFactories(), this.cache, propertyInfo().getPropertyType(), value);
}
@Override
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -30,6 +30,7 @@
import javax.jcr.PropertyType;
import javax.jcr.nodetype.NodeType;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.JcrMixLexicon;
/**
@@ -43,7 +44,7 @@
/** The list of mixin node types. */
private final List<JcrNodeType> mixinNodeTypes;
- DnaBuiltinNodeTypeSource( JcrSession session,
+ DnaBuiltinNodeTypeSource( ExecutionContext context,
JcrNodeTypeSource predecessor ) {
super(predecessor);
@@ -53,28 +54,29 @@
JcrNodeType base = findType(JcrNtLexicon.BASE);
if (base == null) {
- String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
- String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+ String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+ String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
}
JcrNodeType referenceable = findType(JcrMixLexicon.REFERENCEABLE);
if (referenceable == null) {
- String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(session.getExecutionContext().getNamespaceRegistry());
- String namespaceTypeName = DnaLexicon.SYSTEM.getString(session.getExecutionContext().getNamespaceRegistry());
+ String baseTypeName = JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry());
+ String namespaceTypeName = DnaLexicon.SYSTEM.getString(context.getNamespaceRegistry());
throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
}
// Stubbing in child node and property definitions for now
JcrNodeType namespace = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
DnaLexicon.NAMESPACE,
Arrays.asList(new NodeType[] {base}),
DnaLexicon.URI,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
DnaLexicon.URI,
OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -88,12 +90,13 @@
NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType namespaces = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
DnaLexicon.NAMESPACES,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
null,
OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -106,12 +109,13 @@
NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType system = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
DnaLexicon.SYSTEM,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
DnaLexicon.NAMESPACES,
OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -123,25 +127,18 @@
new NodeType[] {namespaces})}),
NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
- JcrNodeType root = new JcrNodeType(session, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base, referenceable}),
- NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
- new JcrNodeDefinition(session, null, JcrLexicon.SYSTEM,
- OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
- true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
- new JcrNodeDefinition(session, null, ALL_NODES,
- OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
- false, true, JcrNtLexicon.UNSTRUCTURED,
- new NodeType[] {base}),
+ JcrNodeType root = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, DnaLexicon.ROOT, Arrays.asList(new NodeType[] {base,
+ referenceable}), NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
+ new JcrNodeDefinition(context, null, JcrLexicon.SYSTEM, OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
+ true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
+ new JcrNodeDefinition(context, null, ALL_NODES, OnParentVersionBehavior.VERSION.getJcrValue(), false, false, false,
+ true, JcrNtLexicon.UNSTRUCTURED, new NodeType[] {base}),
- }), Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, ALL_NODES,
- OnParentVersionBehavior.COPY.getJcrValue(), false,
- false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
- NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, ALL_NODES,
- OnParentVersionBehavior.COPY.getJcrValue(), false,
- false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
- NO_CONSTRAINTS, true),}), NOT_MIXIN,
+ }), Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(context, null, ALL_NODES, OnParentVersionBehavior.COPY.getJcrValue(), false, false, false,
+ NO_DEFAULT_VALUES, PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
+ new JcrPropertyDefinition(context, null, ALL_NODES, OnParentVersionBehavior.COPY.getJcrValue(), false, false, false,
+ NO_DEFAULT_VALUES, PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}), NOT_MIXIN,
ORDERABLE_CHILD_NODES);
primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrBuiltinNodeTypeSource.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -31,6 +31,7 @@
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.basic.BasicName;
@@ -45,27 +46,30 @@
/** The list of mixin node types. */
private final List<JcrNodeType> mixinNodeTypes;
- JcrBuiltinNodeTypeSource( JcrSession session ) {
- this(session, null);
+ JcrBuiltinNodeTypeSource( ExecutionContext context ) {
+ this(context, null);
}
- JcrBuiltinNodeTypeSource( JcrSession session,
+ JcrBuiltinNodeTypeSource( ExecutionContext context,
JcrNodeTypeSource predecessor ) {
super(predecessor);
primaryNodeTypes = new ArrayList<JcrNodeType>();
- Value trueValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.BOOLEAN, Boolean.TRUE);
- Value ntBaseValue = new JcrValue(session.getExecutionContext().getValueFactories(), PropertyType.NAME, JcrNtLexicon.BASE);
+ /*
+ * These values get created without a session cache, as they aren't tied to any particular session.
+ */
+ Value trueValue = new JcrValue(context.getValueFactories(), null, PropertyType.BOOLEAN, Boolean.TRUE);
+ Value ntBaseValue = new JcrValue(context.getValueFactories(), null, PropertyType.NAME, JcrNtLexicon.BASE);
// Stubbing in child node and property definitions for now
- JcrNodeType base = new JcrNodeType(session, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
- Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_TYPE,
+ JcrNodeType base = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.BASE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME,
+ NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_TYPE,
OnParentVersionBehavior.COMPUTE.getJcrValue(), true,
true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.MIXIN_TYPES,
+ new JcrPropertyDefinition(context, null, JcrLexicon.MIXIN_TYPES,
OnParentVersionBehavior.COMPUTE.getJcrValue(), false,
false, true, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, true)}), NOT_MIXIN,
@@ -73,13 +77,14 @@
// This needs to be declared early, as some of the primary types reference it
JcrNodeType referenceable = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrMixLexicon.REFERENCEABLE,
NO_SUPERTYPES,
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.UUID,
OnParentVersionBehavior.INITIALIZE.getJcrValue(),
@@ -93,14 +98,15 @@
IS_A_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType childNodeDefinition = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.CHILD_NODE_DEFINITION,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.AUTO_CREATED,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -108,14 +114,14 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.DEFAULT_PRIMARY_TYPE,
OnParentVersionBehavior.COPY.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.NAME, NO_CONSTRAINTS, false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.MANDATORY,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -123,14 +129,14 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.NAME,
OnParentVersionBehavior.COPY.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.NAME, NO_CONSTRAINTS, false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.ON_PARENT_VERSION,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -138,7 +144,7 @@
PropertyType.STRING, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.PROTECTED,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -146,7 +152,7 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.REQUIRED_PRIMARY_TYPES,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -154,7 +160,7 @@
new Value[] {ntBaseValue},
PropertyType.NAME, NO_CONSTRAINTS, true),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.SAME_NAME_SIBLINGS,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -164,13 +170,14 @@
UNORDERABLE_CHILD_NODES);
JcrNodeType hierarchyNode = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.HIERARCHY_NODE,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.CREATED,
OnParentVersionBehavior.INITIALIZE.getJcrValue(),
@@ -184,12 +191,13 @@
NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType file = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.FILE,
Arrays.asList(new NodeType[] {hierarchyNode}),
JcrLexicon.CONTENT,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
JcrLexicon.CONTENT,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -199,12 +207,13 @@
NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType folder = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.FOLDER,
Arrays.asList(new NodeType[] {hierarchyNode}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
null,
OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -217,12 +226,13 @@
NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType frozenNode = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.FROZEN_NODE,
Arrays.asList(new NodeType[] {base, referenceable}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
ALL_NODES,
OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -233,36 +243,37 @@
null,
new NodeType[] {base})}),
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_MIXIN_TYPES,
+ new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_MIXIN_TYPES,
OnParentVersionBehavior.ABORT.getJcrValue(),
false, false, true, NO_DEFAULT_VALUES,
PropertyType.NAME, NO_CONSTRAINTS, true),
- new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_PRIMARY_TYPE,
+ new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_PRIMARY_TYPE,
OnParentVersionBehavior.ABORT.getJcrValue(), true,
true, true, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.FROZEN_UUID,
+ new JcrPropertyDefinition(context, null, JcrLexicon.FROZEN_UUID,
OnParentVersionBehavior.ABORT.getJcrValue(), true,
true, true, NO_DEFAULT_VALUES,
PropertyType.STRING, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, ALL_NODES,
+ new JcrPropertyDefinition(context, null, ALL_NODES,
OnParentVersionBehavior.ABORT.getJcrValue(),
false, false, true, NO_DEFAULT_VALUES,
PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, ALL_NODES,
+ new JcrPropertyDefinition(context, null, ALL_NODES,
OnParentVersionBehavior.ABORT.getJcrValue(),
false, false, true, NO_DEFAULT_VALUES,
PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
NOT_MIXIN, ORDERABLE_CHILD_NODES);
JcrNodeType linkedFile = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.LINKED_FILE,
Arrays.asList(new NodeType[] {hierarchyNode}),
JcrLexicon.CONTENT,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.CONTENT,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -277,14 +288,15 @@
// Had to be moved above nodeType due to dependency
JcrNodeType propertyDefinition = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.PROPERTY_DEFINITION,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.AUTO_CREATED,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -292,7 +304,7 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.DEFAULT_VALUES,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -300,7 +312,7 @@
PropertyType.UNDEFINED, NO_CONSTRAINTS,
true),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.MANDATORY,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -308,7 +320,7 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.MULTIPLE,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -316,21 +328,21 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.NAME,
OnParentVersionBehavior.COPY.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.NAME, NO_CONSTRAINTS, false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.ON_PARENT_VERSION,
OnParentVersionBehavior.COPY.getJcrValue(),
false, true, false, NO_DEFAULT_VALUES,
PropertyType.STRING, NO_CONSTRAINTS, false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.PROTECTED,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -338,14 +350,14 @@
PropertyType.BOOLEAN, NO_CONSTRAINTS,
false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.REQUIRED_TYPE,
OnParentVersionBehavior.COPY.getJcrValue(),
false, true, false, NO_DEFAULT_VALUES,
PropertyType.STRING, NO_CONSTRAINTS, false),
new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.VALUE_CONSTRAINTS,
OnParentVersionBehavior.COPY.getJcrValue(),
@@ -353,79 +365,82 @@
PropertyType.STRING, NO_CONSTRAINTS, true)}),
NOT_MIXIN, UNORDERABLE_CHILD_NODES);
- JcrNodeType nodeType = new JcrNodeType(session, JcrNtLexicon.NODE_TYPE, Arrays.asList(new NodeType[] {base}),
- NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
- new JcrNodeDefinition(session, null, JcrLexicon.CHILD_NODE_DEFINITION,
+ JcrNodeType nodeType = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.NODE_TYPE,
+ Arrays.asList(new NodeType[] {base}), NO_PRIMARY_ITEM_NAME,
+ Arrays.asList(new JcrNodeDefinition[] {
+ new JcrNodeDefinition(context, null, JcrLexicon.CHILD_NODE_DEFINITION,
OnParentVersionBehavior.VERSION.getJcrValue(), false,
false, false, true, JcrNtLexicon.CHILD_NODE_DEFINITION,
new NodeType[] {childNodeDefinition}),
- new JcrNodeDefinition(session, null, JcrLexicon.PROPERTY_DEFINITION,
+ new JcrNodeDefinition(context, null, JcrLexicon.PROPERTY_DEFINITION,
OnParentVersionBehavior.VERSION.getJcrValue(), false,
false, false, true, JcrNtLexicon.PROPERTY_DEFINITION,
new NodeType[] {propertyDefinition})}),
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
+ new JcrPropertyDefinition(context, null, JcrLexicon.HAS_ORDERABLE_CHILD_NODES,
OnParentVersionBehavior.COPY.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES,
PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.IS_MIXIN,
+ new JcrPropertyDefinition(context, null, JcrLexicon.IS_MIXIN,
OnParentVersionBehavior.COPY.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES,
PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.NODE_TYPE_NAME,
+ new JcrPropertyDefinition(context, null, JcrLexicon.NODE_TYPE_NAME,
OnParentVersionBehavior.COPY.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.PRIMARY_ITEM_NAME,
+ new JcrPropertyDefinition(context, null, JcrLexicon.PRIMARY_ITEM_NAME,
OnParentVersionBehavior.COPY.getJcrValue(), false,
false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.SUPERTYPES,
+ new JcrPropertyDefinition(context, null, JcrLexicon.SUPERTYPES,
OnParentVersionBehavior.COPY.getJcrValue(), false,
false, false, NO_DEFAULT_VALUES, PropertyType.NAME,
NO_CONSTRAINTS, true),}), NOT_MIXIN,
UNORDERABLE_CHILD_NODES);
- JcrNodeType query = new JcrNodeType(session, JcrNtLexicon.QUERY, Arrays.asList(new NodeType[] {base}),
- NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.LANGUAGE,
+ JcrNodeType query = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.QUERY,
+ Arrays.asList(new NodeType[] {base}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+ Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(context, null, JcrLexicon.LANGUAGE,
OnParentVersionBehavior.COPY.getJcrValue(), false,
false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.STATEMENT,
+ new JcrPropertyDefinition(context, null, JcrLexicon.STATEMENT,
OnParentVersionBehavior.COPY.getJcrValue(), false,
false, false, NO_DEFAULT_VALUES, PropertyType.STRING,
NO_CONSTRAINTS, false),}), NOT_MIXIN,
UNORDERABLE_CHILD_NODES);
- JcrNodeType resource = new JcrNodeType(session, JcrNtLexicon.RESOURCE,
+ JcrNodeType resource = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrNtLexicon.RESOURCE,
Arrays.asList(new NodeType[] {base, referenceable}), JcrLexicon.DATA,
NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.DATA,
+ new JcrPropertyDefinition(context, null, JcrLexicon.DATA,
OnParentVersionBehavior.COPY.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES, PropertyType.BINARY,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.ENCODING,
+ new JcrPropertyDefinition(context, null, JcrLexicon.ENCODING,
OnParentVersionBehavior.COPY.getJcrValue(), false,
false, false, NO_DEFAULT_VALUES,
PropertyType.STRING, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.LAST_MODIFIED,
+ new JcrPropertyDefinition(context, null, JcrLexicon.LAST_MODIFIED,
OnParentVersionBehavior.IGNORE.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES, PropertyType.DATE,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.MIMETYPE,
+ new JcrPropertyDefinition(context, null, JcrLexicon.MIMETYPE,
OnParentVersionBehavior.COPY.getJcrValue(), false,
true, false, NO_DEFAULT_VALUES, PropertyType.STRING,
NO_CONSTRAINTS, false),}), NOT_MIXIN,
UNORDERABLE_CHILD_NODES);
JcrNodeType unstructured = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.UNSTRUCTURED,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
ALL_NODES,
OnParentVersionBehavior.VERSION.getJcrValue(),
@@ -436,23 +451,24 @@
JcrNtLexicon.UNSTRUCTURED,
new NodeType[] {base}),}),
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, ALL_NODES,
+ new JcrPropertyDefinition(context, null, ALL_NODES,
OnParentVersionBehavior.COPY.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.UNDEFINED, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, ALL_NODES,
+ new JcrPropertyDefinition(context, null, ALL_NODES,
OnParentVersionBehavior.COPY.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.UNDEFINED, NO_CONSTRAINTS, true),}),
NOT_MIXIN, ORDERABLE_CHILD_NODES);
JcrNodeType version = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.VERSION,
Arrays.asList(new NodeType[] {base, referenceable}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {new JcrNodeDefinition(
- session,
+ context,
null,
JcrLexicon.FROZEN_NODE,
OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -463,28 +479,29 @@
null,
new NodeType[] {frozenNode}),}),
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.CREATED,
+ new JcrPropertyDefinition(context, null, JcrLexicon.CREATED,
OnParentVersionBehavior.ABORT.getJcrValue(), true,
true, true, NO_DEFAULT_VALUES, PropertyType.DATE,
NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+ new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
OnParentVersionBehavior.ABORT.getJcrValue(), false,
false, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, true),
- new JcrPropertyDefinition(session, null, JcrLexicon.SUCCESSORS,
+ new JcrPropertyDefinition(context, null, JcrLexicon.SUCCESSORS,
OnParentVersionBehavior.ABORT.getJcrValue(), false,
false, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, true),}),
NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType versionLabels = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.VERSION_LABELS,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
ALL_NODES,
OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -498,25 +515,26 @@
NOT_MIXIN, UNORDERABLE_CHILD_NODES);
JcrNodeType versionHistory = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.VERSION_HISTORY,
Arrays.asList(new NodeType[] {base, referenceable}),
NO_PRIMARY_ITEM_NAME,
Arrays.asList(new JcrNodeDefinition[] {
- new JcrNodeDefinition(session, null, JcrLexicon.ROOT_VERSION,
+ new JcrNodeDefinition(context, null, JcrLexicon.ROOT_VERSION,
OnParentVersionBehavior.ABORT.getJcrValue(), true,
true, true, false, JcrNtLexicon.VERSION,
new NodeType[] {version}),
- new JcrNodeDefinition(session, null, JcrLexicon.VERSION_LABELS,
+ new JcrNodeDefinition(context, null, JcrLexicon.VERSION_LABELS,
OnParentVersionBehavior.ABORT.getJcrValue(), true,
true, true, false, JcrNtLexicon.VERSION_LABELS,
new NodeType[] {versionLabels}),
- new JcrNodeDefinition(session, null, ALL_NODES,
+ new JcrNodeDefinition(context, null, ALL_NODES,
OnParentVersionBehavior.ABORT.getJcrValue(),
false, false, true, false, JcrNtLexicon.VERSION,
new NodeType[] {version}),}),
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
JcrLexicon.VERSIONABLE_UUID,
OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -531,13 +549,14 @@
Name CHILD_VERSION_HISTORY = new BasicName(JcrLexicon.Namespace.URI, "childVersionHistory");
JcrNodeType versionedChild = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrNtLexicon.VERSIONED_CHILD,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {new JcrPropertyDefinition(
- session,
+ context,
null,
CHILD_VERSION_HISTORY,
OnParentVersionBehavior.ABORT.getJcrValue(),
@@ -556,42 +575,43 @@
mixinNodeTypes = new ArrayList<JcrNodeType>();
- JcrNodeType lockable = new JcrNodeType(session, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES, NO_PRIMARY_ITEM_NAME,
- NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_IS_DEEP,
+ JcrNodeType lockable = new JcrNodeType(context, NO_NODE_TYPE_MANAGER, JcrMixLexicon.LOCKABLE, NO_SUPERTYPES,
+ NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(context, null, JcrLexicon.LOCK_IS_DEEP,
OnParentVersionBehavior.IGNORE.getJcrValue(), false,
false, true, NO_DEFAULT_VALUES,
PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.LOCK_OWNER,
+ new JcrPropertyDefinition(context, null, JcrLexicon.LOCK_OWNER,
OnParentVersionBehavior.IGNORE.getJcrValue(), false,
false, true, NO_DEFAULT_VALUES, PropertyType.STRING,
NO_CONSTRAINTS, false)}), IS_A_MIXIN,
UNORDERABLE_CHILD_NODES);
JcrNodeType versionable = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
JcrMixLexicon.VERSIONABLE,
Arrays.asList(new NodeType[] {referenceable}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null, JcrLexicon.BASE_VERSION,
+ new JcrPropertyDefinition(context, null, JcrLexicon.BASE_VERSION,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, true, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.IS_CHECKED_OUT,
+ new JcrPropertyDefinition(context, null, JcrLexicon.IS_CHECKED_OUT,
OnParentVersionBehavior.IGNORE.getJcrValue(),
true, true, true, new Value[] {trueValue},
PropertyType.BOOLEAN, NO_CONSTRAINTS, false),
- new JcrPropertyDefinition(session, null, JcrLexicon.MERGE_FAILED,
+ new JcrPropertyDefinition(context, null, JcrLexicon.MERGE_FAILED,
OnParentVersionBehavior.ABORT.getJcrValue(),
false, false, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, true),
- new JcrPropertyDefinition(session, null, JcrLexicon.PREDECESSORS,
+ new JcrPropertyDefinition(context, null, JcrLexicon.PREDECESSORS,
OnParentVersionBehavior.COPY.getJcrValue(),
false, true, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, true),
- new JcrPropertyDefinition(session, null, JcrLexicon.VERSION_HISTORY,
+ new JcrPropertyDefinition(context, null, JcrLexicon.VERSION_HISTORY,
OnParentVersionBehavior.COPY.getJcrValue(),
false, true, true, NO_DEFAULT_VALUES,
PropertyType.REFERENCE, NO_CONSTRAINTS, false),}),
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -26,6 +26,7 @@
import javax.jcr.nodetype.ItemDefinition;
import javax.jcr.nodetype.NodeType;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
/**
@@ -35,7 +36,7 @@
@Immutable
class JcrItemDefinition implements ItemDefinition {
- protected final JcrSession session;
+ protected final ExecutionContext context;
protected final JcrNodeType declaringNodeType;
protected final Name name;
@@ -44,7 +45,7 @@
private final boolean mandatory;
private final boolean protectedItem;
- JcrItemDefinition( JcrSession session,
+ JcrItemDefinition( ExecutionContext context,
JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
@@ -52,12 +53,9 @@
boolean mandatory,
boolean protectedItem ) {
super();
- this.session = session;
+ this.context = context;
this.declaringNodeType = declaringNodeType;
- this.name = name != null ? name : session.getExecutionContext()
- .getValueFactories()
- .getNameFactory()
- .create(JcrNodeType.RESIDUAL_ITEM_NAME);
+ this.name = name != null ? name : context.getValueFactories().getNameFactory().create(JcrNodeType.RESIDUAL_ITEM_NAME) ;
this.onParentVersion = onParentVersion;
this.autoCreated = autoCreated;
this.mandatory = mandatory;
@@ -87,7 +85,7 @@
return JcrNodeType.RESIDUAL_ITEM_NAME;
}
- return name.getString(session.getExecutionContext().getNamespaceRegistry());
+ return name.getString(context.getNamespaceRegistry());
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -23,10 +23,10 @@
*/
package org.jboss.dna.jcr;
-import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
/**
@@ -50,7 +50,10 @@
/** A durable identifier for this node definition. */
private NodeDefinitionId id;
- JcrNodeDefinition( JcrSession session,
+ /** Link to the repository node type manager */
+ private final RepositoryNodeTypeManager nodeTypeManager;
+
+ JcrNodeDefinition( ExecutionContext context,
JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
@@ -60,12 +63,31 @@
boolean allowsSameNameSiblings,
Name defaultPrimaryTypeName,
NodeType[] requiredPrimaryTypes ) {
- super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+ super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+ this.nodeTypeManager = null;
this.allowsSameNameSiblings = allowsSameNameSiblings;
this.defaultPrimaryTypeName = defaultPrimaryTypeName;
this.requiredPrimaryTypes = requiredPrimaryTypes;
}
+ JcrNodeDefinition( ExecutionContext context,
+ RepositoryNodeTypeManager nodeTypeManager,
+ JcrNodeType declaringNodeType,
+ Name name,
+ int onParentVersion,
+ boolean autoCreated,
+ boolean mandatory,
+ boolean protectedItem,
+ boolean allowsSameNameSiblings,
+ Name defaultPrimaryTypeName,
+ NodeType[] requiredPrimaryTypes ) {
+ super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+ this.nodeTypeManager = nodeTypeManager;
+ this.allowsSameNameSiblings = allowsSameNameSiblings;
+ this.defaultPrimaryTypeName = defaultPrimaryTypeName;
+ this.requiredPrimaryTypes = requiredPrimaryTypes;
+ }
+
/**
* Get the durable identifier for this node definition.
*
@@ -98,21 +120,7 @@
return null;
}
- /*
- * Translate the name to a prefixed type based on the current transient (session) and persistent (workspace)
- * prefix to URI mappings.
- */
- String mappedTypeName = defaultPrimaryTypeName.getString(session.getExecutionContext().getNamespaceRegistry());
-
- try {
- return session.getWorkspace().getNodeTypeManager().getNodeType(mappedTypeName);
- } catch (RepositoryException re) {
- /*
- * The spec doesn't allow us to throw a checked exception at this point, but a corrupted namespace mapping
- * would be pretty severe.
- */
- throw new IllegalStateException(JcrI18n.typeNotFound.text(mappedTypeName));
- }
+ return nodeTypeManager.getNodeType(defaultPrimaryTypeName);
}
/**
@@ -133,7 +141,15 @@
* <code>declaringNodeType</code>.
*/
JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
- return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), isAutoCreated(), isMandatory(),
- isProtected(), allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
+ return new JcrNodeDefinition(this.context, declaringNodeType.nodeTypeManager(), declaringNodeType, name,
+ getOnParentVersion(), isAutoCreated(), isMandatory(), isProtected(),
+ allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
}
+
+ JcrNodeDefinition with( ExecutionContext context ) {
+ return new JcrNodeDefinition(context, this.nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
+ isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
+ defaultPrimaryTypeName, requiredPrimaryTypes);
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -30,13 +30,13 @@
import java.util.Set;
import java.util.Stack;
import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.Immutable;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
/**
@@ -65,12 +65,16 @@
private boolean orderableChildNodes;
/**
- * A reference to the session in which this node type exists, used to remap the internal names to their appropriate prefixed
- * version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>"nt:base"</code>.).
+ * A reference to the execution context in which this node type exists, used to remap the internal names to their appropriate
+ * prefixed version (e.g., <code>{http://www.jcp.org/jcr/nt/1.0}base</code> to <code>"nt:base"</code>.).
*/
- private JcrSession session;
+ private ExecutionContext context;
- JcrNodeType( JcrSession session,
+ /** Link to the repository node type manager for the repository to which this node type belongs. */
+ private RepositoryNodeTypeManager nodeTypeManager;
+
+ JcrNodeType( ExecutionContext context,
+ RepositoryNodeTypeManager nodeTypeManager,
Name name,
List<NodeType> declaredSupertypes,
Name primaryItemName,
@@ -78,7 +82,8 @@
Collection<JcrPropertyDefinition> propertyDefinitions,
boolean mixin,
boolean orderableChildNodes ) {
- this.session = session;
+ this.context = context;
+ this.nodeTypeManager = nodeTypeManager;
this.name = name;
this.primaryItemName = primaryItemName;
this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<NodeType>emptyList();
@@ -224,35 +229,6 @@
* @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
* are not allowed given this node type
*/
- @Deprecated
- JcrNodeDefinition findBestNodeDefinitionForChild( String childName,
- String primaryNodeTypeName ) {
- // First, try to find a child node definition with the given name
- JcrNodeDefinition childNode = getChildNodeDefinition(childName);
-
- // If there are no named definitions in the type hierarchy, try to find a residual node definition
- if (childNode == null) {
- childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
- }
-
- // Check if the node can be added with the named child node definition
- if (childNode != null && primaryNodeTypeName != null) {
- NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
- if (primaryNodeType == null) return null;
- if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) return null;
- }
- return childNode;
- }
-
- /**
- * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
- * primary type is not supplied, then only the name is considered when finding a best match.
- *
- * @param childName the name of the child
- * @param primaryNodeTypeName the name of the primary node type for the child
- * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
- * are not allowed given this node type
- */
JcrNodeDefinition findBestNodeDefinitionForChild( Name childName,
Name primaryNodeTypeName ) {
// First, try to find a child node definition with the given name
@@ -314,17 +290,8 @@
return false;
}
- protected final NodeType getPrimaryNodeType( String primaryNodeTypeName ) {
- try {
- return session.getWorkspace().getNodeTypeManager().getNodeType(primaryNodeTypeName);
- } catch (RepositoryException re) {
- // If the node type doesn't exist, you can't add a child node with that type
- return null;
- }
- }
-
protected final NodeType getPrimaryNodeType( Name primaryNodeTypeName ) {
- return session.nodeTypeManager().getNodeType(primaryNodeTypeName);
+ return nodeTypeManager.getNodeType(primaryNodeTypeName);
}
/**
@@ -338,7 +305,8 @@
CheckArg.isNotNull(childNodeName, "childNodeName");
CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
- NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+ Name nodeTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
+ NodeType primaryNodeType = getPrimaryNodeType(nodeTypeName);
if (primaryNodeType == null) {
// The node type doesn't exist, so you can't add a child node with that type
return false;
@@ -585,7 +553,7 @@
*/
public String getName() {
// Translate the name to the correct prefix. Need to check the session to support url-remapping.
- return name.getString(session.getExecutionContext().getNamespaceRegistry());
+ return name.getString(context.getNamespaceRegistry());
}
/**
@@ -609,7 +577,7 @@
}
// Translate the name to the correct prefix. Need to check the session to support url-remapping.
- return primaryItemName.getString(session.getExecutionContext().getNamespaceRegistry());
+ return primaryItemName.getString(context.getNamespaceRegistry());
}
/**
@@ -695,4 +663,33 @@
public String toString() {
return getName();
}
+
+ /**
+ * Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different repository node type
+ * manager. This method should only be called during the initialization of the repository node type manager, unless some kind
+ * of cross-repository type shipping is implemented.
+ *
+ * @param nodeTypeManager the new repository node type manager
+ * @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
+ */
+ final JcrNodeType with( RepositoryNodeTypeManager nodeTypeManager ) {
+ return new JcrNodeType(this.context, nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
+ this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.orderableChildNodes);
+ }
+
+ /**
+ * Returns a {@link JcrNodeType} that is equivalent to this {@link JcrNodeType}, except with a different execution context.
+ *
+ * @param context the new execution context
+ * @return a new {@link JcrNodeType} that has the same state as this node type, but with the given node type manager.
+ * @see JcrNodeTypeManager
+ */
+ final JcrNodeType with( ExecutionContext context ) {
+ return new JcrNodeType(context, this.nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
+ this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.orderableChildNodes);
+ }
+
+ final RepositoryNodeTypeManager nodeTypeManager() {
+ return nodeTypeManager;
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -25,44 +25,35 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.NodeTypeManager;
-import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
/**
- * Local implementation of @{link NodeTypeManager}. Initialized with {@link NodeType} source data when it is created (in the
- * {@link JcrWorkspace} constructor.
+ * Local implementation of @{link NodeTypeManager}. This class handles translation between {@link Name}s and {@link String}s based
+ * on the namespace registry from the session's execution context in order to support transient namespace remappings. All
+ * {@link NodeType}s returned by this implementation are wrapped with the execution context of the session to allow proper ongoing
+ * handling of names. This implies that reference equality is not a safe test for node type equivalence.
+ *
+ * @see RepositoryNodeTypeManager
*/
-@NotThreadSafe
+@Immutable
class JcrNodeTypeManager implements NodeTypeManager {
- private final Map<Name, JcrNodeType> primaryNodeTypes;
- private final Map<Name, JcrNodeType> mixinNodeTypes;
- private final JcrSession session;
+ private final ExecutionContext context;
+ private final RepositoryNodeTypeManager repositoryTypeManager;
- JcrNodeTypeManager( JcrSession session,
- JcrNodeTypeSource source ) {
- this.session = session;
- Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
- Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
-
- primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
- for (JcrNodeType nodeType : primary) {
- primaryNodeTypes.put(nodeType.getInternalName(), nodeType);
- }
-
- mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
- for (JcrNodeType nodeType : mixins) {
- mixinNodeTypes.put(nodeType.getInternalName(), nodeType);
- }
+ JcrNodeTypeManager( ExecutionContext context,
+ RepositoryNodeTypeManager repositoryTypeManager ) {
+ this.context = context;
+ this.repositoryTypeManager = repositoryTypeManager;
}
/**
@@ -72,6 +63,9 @@
*/
public NodeTypeIterator getAllNodeTypes() {
+ Collection<JcrNodeType> mixinNodeTypes = repositoryTypeManager.getMixinNodeTypes();
+ Collection<JcrNodeType> primaryNodeTypes = repositoryTypeManager.getPrimaryNodeTypes();
+
// TODO: Can revisit this approach later if it becomes a performance issue
/*
* Note also that this creates a subtle difference in behavior for concurrent modification
@@ -82,8 +76,16 @@
*/
List<NodeType> allTypes = new ArrayList<NodeType>(primaryNodeTypes.size() + mixinNodeTypes.size());
- allTypes.addAll(primaryNodeTypes.values());
- allTypes.addAll(mixinNodeTypes.values());
+
+ // Need to return a version of the node type with the current context
+ for (JcrNodeType type : primaryNodeTypes) {
+ allTypes.add(type.with(context));
+ }
+
+ for (JcrNodeType type : mixinNodeTypes) {
+ allTypes.add(type.with(context));
+ }
+
return new JcrNodeTypeIterator(allTypes);
}
@@ -93,16 +95,15 @@
* @see javax.jcr.nodetype.NodeTypeManager#getMixinNodeTypes()
*/
public NodeTypeIterator getMixinNodeTypes() {
- return new JcrNodeTypeIterator(mixinNodeTypes.values());
- }
+ Collection<JcrNodeType> rawTypes = repositoryTypeManager.getMixinNodeTypes();
+ List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
- JcrNodeType getNodeType( Name nodeTypeName ) {
+ // Need to return a version of the node type with the current context
+ for (JcrNodeType type : rawTypes) {
+ types.add(type.with(context));
+ }
- JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
- if (nodeType == null) {
- nodeType = mixinNodeTypes.get(nodeTypeName);
- }
- return nodeType;
+ return new JcrNodeTypeIterator(types);
}
/**
@@ -111,19 +112,47 @@
* @see javax.jcr.nodetype.NodeTypeManager#getNodeType(java.lang.String)
*/
public NodeType getNodeType( String nodeTypeName ) throws NoSuchNodeTypeException, RepositoryException {
- Name ntName = session.getExecutionContext().getValueFactories().getNameFactory().create(nodeTypeName);
- NodeType type = getNodeType(ntName);
- if (type != null) return type;
+ Name ntName = context.getValueFactories().getNameFactory().create(nodeTypeName);
+ JcrNodeType type = repositoryTypeManager.getNodeType(ntName);
+ if (type != null) {
+ type = type.with(context);
+ return type;
+ }
throw new NoSuchNodeTypeException(JcrI18n.typeNotFound.text(nodeTypeName));
}
/**
+ * Returns the node type with the given name (if one exists)
+ *
+ * @param nodeTypeName the name of the node type to be returned
+ * @return the node type with the given name (if one exists)
+ * @see RepositoryNodeTypeManager#getNodeType(Name)
+ */
+ JcrNodeType getNodeType( Name nodeTypeName ) {
+ JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
+
+ if (nodeType != null) {
+ nodeType = nodeType.with(context);
+ }
+
+ return nodeType;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeTypeManager#getPrimaryNodeTypes()
*/
public NodeTypeIterator getPrimaryNodeTypes() {
- return new JcrNodeTypeIterator(primaryNodeTypes.values());
+ Collection<JcrNodeType> rawTypes = repositoryTypeManager.getPrimaryNodeTypes();
+ List<JcrNodeType> types = new ArrayList<JcrNodeType>(rawTypes.size());
+
+ // Need to return a version of the node type with the current context
+ for (JcrNodeType type : rawTypes) {
+ types.add(type.with(context));
+ }
+
+ return new JcrNodeTypeIterator(types);
}
/**
@@ -134,7 +163,7 @@
* @throws NoSuchNodeTypeException
*/
JcrNodeDefinition getRootNodeDefinition() throws NoSuchNodeTypeException, RepositoryException {
- for (NodeDefinition definition : getNodeType(DnaLexicon.ROOT).getChildNodeDefinitions()) {
+ for (NodeDefinition definition : repositoryTypeManager.getNodeType(DnaLexicon.ROOT).getChildNodeDefinitions()) {
if (definition.getName().equals(JcrNodeType.RESIDUAL_ITEM_NAME)) return (JcrNodeDefinition)definition;
}
assert false; // should not get here
@@ -150,7 +179,7 @@
JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
if (definitionId == null) return null;
Name nodeTypeName = definitionId.getNodeTypeName();
- JcrNodeType nodeType = getNodeType(nodeTypeName);
+ JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
return nodeType.getChildNodeDefinition(definitionId.getChildDefinitionName());
}
@@ -165,7 +194,7 @@
boolean prefersMultiValued ) {
if (definitionId == null) return null;
Name nodeTypeName = definitionId.getNodeTypeName();
- JcrNodeType nodeType = getNodeType(nodeTypeName);
+ JcrNodeType nodeType = repositoryTypeManager.getNodeType(nodeTypeName);
return nodeType.getPropertyDefinition(definitionId.getPropertyDefinitionName(), prefersMultiValued);
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.jcr;
+import java.util.UUID;
import java.util.regex.Pattern;
import javax.jcr.Node;
import javax.jcr.PropertyType;
@@ -41,6 +42,7 @@
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.ValueFormatException;
import org.jboss.dna.graph.property.basic.JodaDateTime;
+
/**
* DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
* through its constructor.
@@ -55,7 +57,7 @@
private PropertyDefinitionId id;
private ConstraintChecker checker = null;
- JcrPropertyDefinition( JcrSession session,
+ JcrPropertyDefinition( ExecutionContext context,
JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
@@ -66,7 +68,7 @@
int requiredType,
String[] valueConstraints,
boolean multiple ) {
- super(session, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
+ super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
this.defaultValues = defaultValues;
this.requiredType = requiredType;
this.valueConstraints = valueConstraints;
@@ -130,7 +132,7 @@
* <code>declaringNodeType</code>.
*/
JcrPropertyDefinition with( JcrNodeType declaringNodeType ) {
- return new JcrPropertyDefinition(this.session, declaringNodeType, this.name, this.getOnParentVersion(),
+ return new JcrPropertyDefinition(this.context, declaringNodeType, this.name, this.getOnParentVersion(),
this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
}
@@ -152,7 +154,7 @@
ConstraintChecker checker = this.checker;
if (checker == null || checker.getType() != type) {
- checker = createChecker(session, type, valueConstraints);
+ checker = createChecker(context, type, valueConstraints);
this.checker = checker;
}
@@ -167,21 +169,18 @@
/**
* Returns a {@link ConstraintChecker} that will interpret the constraints described by <code>valueConstraints</code> using
* the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type indicated by <code>type</code> (where
- * <code>type</code> is a value from {@link PropertyType}) for the given <code>session</code>. The session is required to
- * handle the UUID lookups for reference constraints and provides the {@link ExecutionContext} that is used to provide
- * namespace mappings and value factories for the other constraint checkers.
+ * <code>type</code> is a value from {@link PropertyType}) for the given <code>context</code>. The {@link ExecutionContext} is
+ * used to provide namespace mappings and value factories for the other constraint checkers.
*
- * @param session the current session
+ * @param context the execution context
* @param type the type of constraint checker that should be created (based on values from {@link PropertyType}).
* Type-specific semantics are defined in section 6.7.16 of the JCR 1.0 specification.
* @param valueConstraints the constraints for the node as provided by {@link PropertyDefinition#getValueConstraints()}.
* @return a constraint checker that matches the given parameters
*/
- private ConstraintChecker createChecker( JcrSession session,
- int type,
- String[] valueConstraints ) {
- ExecutionContext context = session.getExecutionContext();
-
+ private ConstraintChecker createChecker( ExecutionContext context,
+ int type,
+ String[] valueConstraints ) {
switch (type) {
case PropertyType.BINARY:
return new BinaryConstraintChecker(valueConstraints, context);
@@ -196,7 +195,7 @@
case PropertyType.PATH:
return new PathConstraintChecker(valueConstraints, context);
case PropertyType.REFERENCE:
- return new ReferenceConstraintChecker(valueConstraints, session);
+ return new ReferenceConstraintChecker(valueConstraints, context);
case PropertyType.STRING:
return new StringConstraintChecker(valueConstraints, context);
default:
@@ -333,7 +332,8 @@
@Override
public boolean matches( Value value ) {
Binary binary = valueFactories.getBinaryFactory().create(((JcrValue)value).value());
- return super.matches(new JcrValue(valueFactories, PropertyType.LONG, binary.getSize()));
+ Value sizeValue = ((JcrValue)value).sessionCache().session().getValueFactory().createValue(binary.getSize());
+ return super.matches(sizeValue);
}
}
@@ -408,15 +408,15 @@
@Immutable
private static class ReferenceConstraintChecker implements ConstraintChecker {
- private final JcrSession session;
+ // private final ExecutionContext context;
private final Name[] constraints;
protected ReferenceConstraintChecker( String[] valueConstraints,
- JcrSession session ) {
- this.session = session;
+ ExecutionContext context ) {
+ // this.context = context;
- NameFactory factory = session.getExecutionContext().getValueFactories().getNameFactory();
+ NameFactory factory = context.getValueFactories().getNameFactory();
constraints = new Name[valueConstraints.length];
@@ -430,18 +430,21 @@
}
public boolean matches( Value value ) {
- assert value != null;
+ assert value instanceof JcrValue;
+ JcrValue jcrValue = (JcrValue)value;
+ SessionCache cache = jcrValue.sessionCache();
+
Node node = null;
try {
- node = session.getNodeByUUID(((JcrValue)value).value().toString());
+ node = cache.findJcrNode((UUID)jcrValue.value());
} catch (RepositoryException re) {
return false;
}
for (int i = 0; i < constraints.length; i++) {
try {
- if (node.isNodeType(constraints[i].getString(session.getExecutionContext().getNamespaceRegistry()))) {
+ if (node.isNodeType(constraints[i].getString(cache.session().namespaces()))) {
return true;
}
} catch (RepositoryException re) {
@@ -474,9 +477,11 @@
}
public boolean matches( Value value ) {
- assert value != null;
+ assert value instanceof JcrValue;
- Name name = valueFactory.create(((JcrValue)value).value());
+ JcrValue jcrValue = (JcrValue)value;
+ // Need to use the session execution context to handle the remaps
+ Name name = jcrValue.sessionCache().session().getExecutionContext().getValueFactories().getNameFactory().create(jcrValue.value());
for (int i = 0; i < constraints.length; i++) {
if (constraints[i].equals(name)) {
@@ -561,5 +566,4 @@
return false;
}
}
-
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRepository.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -75,6 +75,7 @@
private final Map<String, String> descriptors;
private final ExecutionContext executionContext;
private final RepositoryConnectionFactory connectionFactory;
+ private final RepositoryNodeTypeManager repositoryTypeManager;
/**
* Creates a JCR repository that uses the supplied {@link RepositoryConnectionFactory repository connection factory} to
@@ -145,9 +146,22 @@
modifiableDescriptors.put(Repository.SPEC_NAME_DESC, JcrI18n.SPEC_NAME_DESC.text());
modifiableDescriptors.put(Repository.SPEC_VERSION_DESC, "1.0");
this.descriptors = Collections.unmodifiableMap(modifiableDescriptors);
+
+ JcrNodeTypeSource source = null;
+ source = new JcrBuiltinNodeTypeSource(this.executionContext);
+ source = new DnaBuiltinNodeTypeSource(this.executionContext, source);
+ this.repositoryTypeManager = new RepositoryNodeTypeManager(this.executionContext, source);
}
/**
+ * Returns the repository-level node type manager
+ * @return the repository-level node type manager
+ */
+ RepositoryNodeTypeManager getRepositoryTypeManager() {
+ return repositoryTypeManager;
+ }
+
+ /**
* Get the name of the repository source that this repository is using.
*
* @return the name of the RepositorySource
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -146,6 +146,11 @@
assert this.graph != null;
}
+ // Added to facilitate mock testing of items without necessarily requiring an entire repository structure to be built
+ final SessionCache cache() {
+ return this.cache;
+ }
+
ExecutionContext getExecutionContext() {
return this.executionContext;
}
@@ -439,39 +444,41 @@
*/
public ValueFactory getValueFactory() {
final ValueFactories valueFactories = executionContext.getValueFactories();
+ final SessionCache sessionCache = this.cache;
+
return new ValueFactory() {
public Value createValue( String value,
int propertyType ) {
- return new JcrValue(valueFactories, propertyType, value);
+ return new JcrValue(valueFactories, sessionCache, propertyType, value);
}
public Value createValue( Node value ) throws RepositoryException {
- return new JcrValue(valueFactories, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
+ return new JcrValue(valueFactories, sessionCache, PropertyType.REFERENCE, UUID.fromString(value.getUUID()));
}
public Value createValue( InputStream value ) {
- return new JcrValue(valueFactories, PropertyType.BINARY, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.BINARY, value);
}
public Value createValue( Calendar value ) {
- return new JcrValue(valueFactories, PropertyType.DATE, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.DATE, value);
}
public Value createValue( boolean value ) {
- return new JcrValue(valueFactories, PropertyType.BOOLEAN, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.BOOLEAN, value);
}
public Value createValue( double value ) {
- return new JcrValue(valueFactories, PropertyType.DOUBLE, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.DOUBLE, value);
}
public Value createValue( long value ) {
- return new JcrValue(valueFactories, PropertyType.LONG, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.LONG, value);
}
public Value createValue( String value ) {
- return new JcrValue(valueFactories, PropertyType.STRING, value);
+ return new JcrValue(valueFactories, sessionCache, PropertyType.STRING, value);
}
};
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -43,19 +43,27 @@
@NotThreadSafe
final class JcrValue implements Value {
+ private final SessionCache sessionCache;
private final ValueFactories valueFactories;
private final int type;
private final Object value;
JcrValue( ValueFactories valueFactories,
+ SessionCache sessionCache,
int type,
Object value ) {
assert valueFactories != null;
assert type == PropertyType.BINARY || type == PropertyType.BOOLEAN || type == PropertyType.DATE
|| type == PropertyType.DOUBLE || type == PropertyType.LONG || type == PropertyType.NAME
|| type == PropertyType.PATH || type == PropertyType.REFERENCE || type == PropertyType.STRING;
+
+ // Leaving this assertion out for now so that values can be created in node type sources, which are created outside
+ // the context of any particular session.
+ // assert sessionCache != null;
assert value != null;
+
this.valueFactories = valueFactories;
+ this.sessionCache = sessionCache;
this.type = type;
this.value = value;
}
@@ -81,6 +89,14 @@
}
/**
+ * Returns the session cache for the session that created this value.
+ * @return the session cache for the session that created this value.
+ */
+ final SessionCache sessionCache() {
+ return sessionCache;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.Value#getBoolean()
@@ -195,6 +211,11 @@
return type;
}
+ private JcrValue withTypeAndValue( int type,
+ Object value ) {
+ return new JcrValue(this.valueFactories, this.sessionCache, type, value);
+ }
+
/**
* Returns a copy of the current {@link JcrValue} cast to the JCR type specified by the <code>type</code> argument. If the
* value cannot be converted base don the JCR type conversion rules, a {@link ValueFormatException} will be thrown.
@@ -207,14 +228,14 @@
JcrValue asType( int type ) throws ValueFormatException {
if (type == this.type) {
- return new JcrValue(this.valueFactories, this.type, this.value);
+ return this.withTypeAndValue(this.type, this.value);
}
switch (type) {
case PropertyType.BOOLEAN:
try {
if (this.type == PropertyType.STRING || this.type == PropertyType.BINARY) {
- return new JcrValue(this.valueFactories, type, valueFactories.getBooleanFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getBooleanFactory().create(value));
}
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
throw createValueFormatException(vfe);
@@ -225,7 +246,7 @@
try {
if (this.type == PropertyType.DOUBLE || this.type == PropertyType.LONG || this.type == PropertyType.STRING
|| this.type == PropertyType.BINARY) {
- return new JcrValue(this.valueFactories, type, valueFactories.getDateFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getDateFactory().create(value));
}
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
throw createValueFormatException(vfe);
@@ -236,12 +257,12 @@
case PropertyType.NAME:
try {
if (this.type == PropertyType.STRING) {
- return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getNameFactory().create(value));
}
String valueAsString = this.valueFactories.getStringFactory().create(value);
if (this.type == PropertyType.BINARY) {
- return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory().create(valueAsString));
+ return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
}
@@ -250,9 +271,7 @@
Segment[] segments = path.getSegmentsArray();
if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
- return new JcrValue(this.valueFactories, type,
- this.valueFactories.getNameFactory().create(valueAsString));
-
+ return this.withTypeAndValue(type, valueFactories.getNameFactory().create(valueAsString));
}
}
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
@@ -264,7 +283,8 @@
case PropertyType.PATH:
try {
if (this.type == PropertyType.STRING) {
- return new JcrValue(this.valueFactories, type, this.valueFactories.getPathFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getPathFactory().create(value));
+
}
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
throw createValueFormatException(vfe);
@@ -282,18 +302,21 @@
// Anything can be converted to these types
case PropertyType.BINARY:
try {
- return new JcrValue(this.valueFactories, type, valueFactories.getBinaryFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getBinaryFactory().create(value));
+
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
throw createValueFormatException(vfe);
}
case PropertyType.STRING:
try {
- return new JcrValue(this.valueFactories, type, valueFactories.getStringFactory().create(value));
+ return this.withTypeAndValue(type, valueFactories.getStringFactory().create(value));
+
} catch (org.jboss.dna.graph.property.ValueFormatException vfe) {
throw createValueFormatException(vfe);
}
case PropertyType.UNDEFINED:
- return new JcrValue(this.valueFactories, this.type, this.value);
+ return this.withTypeAndValue(this.type, this.value);
+
default:
assert false : "Unexpected JCR property type " + type;
// This should still throw an exception even if assertions are turned off
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -148,10 +148,7 @@
this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
// This must be initialized after the session
- JcrNodeTypeSource source = null;
- source = new JcrBuiltinNodeTypeSource(this.session);
- source = new DnaBuiltinNodeTypeSource(this.session, source);
- this.nodeTypeManager = new JcrNodeTypeManager(this.session, source);
+ this.nodeTypeManager = new JcrNodeTypeManager(session.getExecutionContext(), repository.getRepositoryTypeManager());
}
final String getSourceName() {
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -0,0 +1,81 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * The {@link RepositoryNodeTypeManager} is the maintainer of node type information for the entire repository at run-time. The
+ * repository manager maintains a list of all node types and the ability to retrieve node types by {@link Name}. </p> The JCR 1.0
+ * and 2.0 specifications both require that node type information be shared across all sessions within a repository and that the
+ * {@link javax.jcr.nodetype.NodeTypeManager} perform operations based on the string versions of {@link Name}s based on the permanent
+ * (workspace-scoped) and transient (session-scoped) namespace mappings. DNA achieves this by maintaining a single master
+ * repository of all node type information (the {@link RepositoryNodeTypeManager}) and per-session wrappers (
+ * {@link JcrNodeTypeManager}) for this master repository that perform {@link String} to {@link Name} translation based on the
+ * {@link javax.jcr.Session}'s transient mappings and then delegating node type lookups to the repository manager.
+ */
+@Immutable
+class RepositoryNodeTypeManager {
+
+ private final Map<Name, JcrNodeType> primaryNodeTypes;
+ private final Map<Name, JcrNodeType> mixinNodeTypes;
+
+ RepositoryNodeTypeManager( ExecutionContext context,
+ JcrNodeTypeSource source ) {
+ Collection<JcrNodeType> primary = source.getPrimaryNodeTypes();
+ Collection<JcrNodeType> mixins = source.getMixinNodeTypes();
+
+ primaryNodeTypes = new HashMap<Name, JcrNodeType>(primary.size());
+ for (JcrNodeType nodeType : primary) {
+ primaryNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+ }
+
+ mixinNodeTypes = new HashMap<Name, JcrNodeType>(mixins.size());
+ for (JcrNodeType nodeType : mixins) {
+ mixinNodeTypes.put(nodeType.getInternalName(), nodeType.with(this));
+ }
+ }
+
+ public Collection<JcrNodeType> getMixinNodeTypes() {
+ return mixinNodeTypes.values();
+ }
+
+ public Collection<JcrNodeType> getPrimaryNodeTypes() {
+ return primaryNodeTypes.values();
+ }
+
+ JcrNodeType getNodeType( Name nodeTypeName ) {
+
+ JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
+ if (nodeType == null) {
+ nodeType = mixinNodeTypes.get(nodeTypeName);
+ }
+ return nodeType;
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/RepositoryNodeTypeManager.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -1441,7 +1441,7 @@
* @param primaryTypeOfChild the name of the child's primary type
* @return the node definition for this child, as best as can be determined, or null if the node definition could not be
* determined
- * @throws RepositoryException if the parent's pimary node type cannot be found in the {@link NodeTypeManager}
+ * @throws RepositoryException if the parent's primary node type cannot be found in the {@link NodeTypeManager}
*/
protected JcrNodeDefinition findNodeDefinitionForChild( NodeInfo parentInfo,
Name childName,
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -151,7 +151,7 @@
}
protected Value stringValueFor( Object value ) {
- return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
+ return new JcrValue(context.getValueFactories(), cache, PropertyType.STRING, value);
}
protected void addChild( Name childName,
@@ -463,37 +463,7 @@
assertThat((JcrSession)node.getSession(), is(session));
}
- @Test
- public void shouldProvideUuidIfReferenceable() throws Exception {
- // Create the property ...
- PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
- AbstractJcrProperty property = mock(AbstractJcrProperty.class);
- stub(cache.findJcrProperty(propertyId)).toReturn(property);
- stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:referenceable")});
- // Call the method ...
- assertThat(node.getUUID(), is(uuid.toString()));
- }
-
@Test( expected = UnsupportedRepositoryOperationException.class )
- public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
- // Create the property ...
- PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
- AbstractJcrProperty property = mock(AbstractJcrProperty.class);
- stub(cache.findJcrProperty(propertyId)).toReturn(property);
- stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:notReferenceable")});
- // Call the method ...
- node.getUUID();
- }
-
- @Test( expected = UnsupportedRepositoryOperationException.class )
- public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
- PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
- stub(cache.findJcrProperty(propertyId)).toReturn(null);
- // Call the method ...
- node.getUUID();
- }
-
- @Test( expected = UnsupportedRepositoryOperationException.class )
public void shouldNotAllowGetVersionHistory() throws Exception {
node.getVersionHistory();
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -30,8 +30,6 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import java.util.UUID;
-import javax.jcr.Node;
import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
@@ -52,21 +50,22 @@
import org.mockito.MockitoAnnotations.Mock;
/**
- * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)},
- * which provides the wrapper around the factory that the rest of the API is expected to utilize.
- *
+ * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)}, which
+ * provides the wrapper around the factory that the rest of the API is expected to utilize.
*/
public class JcrPropertyDefinitionTest {
- private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
- private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] { "[,+1945-08-01T01:30:00.000Z]", "[+1975-08-01T01:30:00.000Z,)"};
- private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] { "[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]" };
- private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
- private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] { "jcr:system", "dnatest:constrainedType" };
- //private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c" };
- private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c" };
- private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] { "dna:root" };
- private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] { "foo", "bar*", ".*baz" };
+ private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+ private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] {"[,+1945-08-01T01:30:00.000Z]",
+ "[+1975-08-01T01:30:00.000Z,)"};
+ private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] {"[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]"};
+ private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] {"[,5)", "[10, 20)", "(30,40]", "[50,]"};
+ private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] {"jcr:system", "dnatest:constrainedType"};
+ // private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b",
+ // "/a/b/c" };
+ private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c"};
+ private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] {"dna:root"};
+ private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] {"foo", "bar*", ".*baz"};
private String workspaceName;
private ExecutionContext context;
@@ -75,6 +74,7 @@
private JcrSession session;
private Graph graph;
private RepositoryConnectionFactory connectionFactory;
+ private RepositoryNodeTypeManager repoTypeManager;
private NodeTypeManager nodeTypeManager;
private Map<String, Object> sessionAttributes;
@Mock
@@ -82,6 +82,8 @@
@Before
public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
workspaceName = "workspace1";
final String repositorySourceName = "repository";
@@ -97,7 +99,7 @@
// Set up the initial content ...
graph = Graph.create(source, context);
-
+
// Make sure the path to the namespaces exists ...
graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
graph.create("/a").and().create("/a/b").and().create("/a/b/c");
@@ -118,7 +120,12 @@
};
// Stub out the repository, since we only need a few methods ...
- MockitoAnnotations.initMocks(this);
+ JcrNodeTypeSource source = null;
+ source = new JcrBuiltinNodeTypeSource(this.context, source);
+ source = new DnaBuiltinNodeTypeSource(this.context, source);
+ source = new TestNodeTypeSource(this.context, source);
+ repoTypeManager = new RepositoryNodeTypeManager(context, source);
+ stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
stub(repository.getConnectionFactory()).toReturn(connectionFactory);
@@ -132,39 +139,37 @@
// Create the session and log in ...
session = (JcrSession)workspace.getSession();
- nodeTypeManager = new JcrNodeTypeManager(this.session,
- new TestNodeTypeSource(this.session,
- new DnaBuiltinNodeTypeSource(this.session,
- new JcrBuiltinNodeTypeSource(this.session))));
- stub(workspace.getNodeTypeManager()).toReturn(nodeTypeManager);
-
+ nodeTypeManager = workspace.getNodeTypeManager();
}
@After
public void after() throws Exception {
- if (session.isLive()) {
+ if (session != null && session.isLive()) {
session.logout();
}
}
- private JcrPropertyDefinition propertyDefinitionFor(NodeType nodeType, Name propertyName) {
+ private JcrPropertyDefinition propertyDefinitionFor( NodeType nodeType,
+ Name propertyName ) {
PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
String property = propertyName.getString(context.getNamespaceRegistry());
-
+
for (int i = 0; i < propertyDefs.length; i++) {
if (propertyDefs[i].getName().equals(property)) {
- return (JcrPropertyDefinition) propertyDefs[i];
+ return (JcrPropertyDefinition)propertyDefs[i];
}
}
- throw new IllegalStateException("Could not find property definition name " + property + " for type "
- + nodeType.getName() + ". Test setup is invalid.");
+ throw new IllegalStateException("Could not find property definition name " + property + " for type " + nodeType.getName()
+ + ". Test setup is invalid.");
}
- private void checkConstraints(NodeType nodeType, Name propertyName, String[] expectedConstraints) {
+ private void checkConstraints( NodeType nodeType,
+ Name propertyName,
+ String[] expectedConstraints ) {
PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
String property = propertyName.getString(context.getNamespaceRegistry());
String[] constraints = null;
-
+
for (int i = 0; i < propertyDefs.length; i++) {
if (propertyDefs[i].getName().equals(property)) {
constraints = propertyDefs[i].getValueConstraints();
@@ -176,10 +181,10 @@
throw new IllegalStateException("Unexpected constraints for property: " + property);
}
}
-
+
private NodeType validateTypeDefinition() throws Exception {
NamespaceRegistry nsr = context.getNamespaceRegistry();
-
+
NodeType constrainedType = nodeTypeManager.getNodeType(TestLexicon.CONSTRAINED_TYPE.getString(nsr));
assertThat(constrainedType, notNullValue());
assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY), notNullValue());
@@ -190,7 +195,7 @@
assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH), notNullValue());
assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE), notNullValue());
assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING), notNullValue());
-
+
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_BINARY, EXPECTED_BINARY_CONSTRAINTS);
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DATE, EXPECTED_DATE_CONSTRAINTS);
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DOUBLE, EXPECTED_DOUBLE_CONSTRAINTS);
@@ -199,24 +204,26 @@
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_PATH, EXPECTED_PATH_CONSTRAINTS);
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_REFERENCE, EXPECTED_REFERENCE_CONSTRAINTS);
checkConstraints(constrainedType, TestLexicon.CONSTRAINED_STRING, EXPECTED_STRING_CONSTRAINTS);
-
+
return constrainedType;
}
-
- private Value valueFor(Object value, int jcrType) {
- return new JcrValue(context.getValueFactories(), jcrType, value);
+
+ private Value valueFor( Object value,
+ int jcrType ) {
+ return new JcrValue(context.getValueFactories(), session.cache(), jcrType, value);
}
-
- private String stringOfLength(int length) {
+
+ private String stringOfLength( int length ) {
StringBuffer buff = new StringBuffer(length);
for (int i = 0; i < length; i++) {
buff.append(i % 10);
}
-
+
return buff.toString();
}
- private boolean satisfiesConstraints(JcrPropertyDefinition property, Value[] values) {
+ private boolean satisfiesConstraints( JcrPropertyDefinition property,
+ Value[] values ) {
for (int i = 0; i < values.length; i++) {
if (!property.satisfiesConstraints(values[i])) {
return false;
@@ -224,8 +231,8 @@
}
return true;
}
-
- @Test( expected = AssertionError.class )
+
+ @Test( expected = AssertionError.class )
public void shouldNotAllowNullValue() throws Exception {
NodeType constrainedType = validateTypeDefinition();
JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
@@ -585,45 +592,41 @@
public void shouldAllowValidReferenceValue() throws Exception {
NodeType constrainedType = validateTypeDefinition();
JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-
- UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
- assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(true));
+ Value value = session.getValueFactory().createValue(session.getRootNode());
+
+ assertThat(prop.satisfiesConstraints(value), is(true));
}
@Test
public void shouldAllowValidReferenceValues() throws Exception {
NodeType constrainedType = validateTypeDefinition();
JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-
- UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
- assertThat(satisfiesConstraints(prop, new Value[] { }), is(true));
- assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(true));
+ Value value = session.getValueFactory().createValue(session.getRootNode());
+
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {value}), is(true));
}
-
@Test
public void shouldNotAllowInvalidReferenceValue() throws Exception {
NodeType constrainedType = validateTypeDefinition();
JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
- Node aNode = session.getRootNode().getNode("a");
- UUID uuid = ((JcrNode) aNode).nodeUuid;
+ Value value = session.getValueFactory().createValue(session.getRootNode().getNode("a"));
-
- assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(false));
+ assertThat(prop.satisfiesConstraints(value), is(false));
}
@Test
public void shouldNotAllowInvalidReferenceValues() throws Exception {
NodeType constrainedType = validateTypeDefinition();
JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
-
- Node aNode = session.getRootNode().getNode("a");
- UUID uuid = ((JcrNode) aNode).nodeUuid;
-
- assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(false));
+
+ Value value = session.getValueFactory().createValue(session.getRootNode().getNode("a"));
+
+ assertThat(satisfiesConstraints(prop, new Value[] {value}), is(false));
}
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -50,7 +50,9 @@
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFactory;
+import javax.jcr.nodetype.NodeType;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import org.jboss.dna.graph.ExecutionContext;
@@ -81,6 +83,7 @@
private JcrSession session;
private Graph graph;
private RepositoryConnectionFactory connectionFactory;
+ private RepositoryNodeTypeManager repoTypeManager;
private Map<String, Object> sessionAttributes;
@Mock
private JcrRepository repository;
@@ -103,6 +106,7 @@
graph.create("/a").and().create("/a/b").and().create("/a/b/c");
graph.set("booleanProperty").on("/a/b").to(true);
graph.set("stringProperty").on("/a/b/c").to("value");
+ graph.set("jcr:mixinTypes").on("/a").to("mix:lockable");
graph.set("jcr:mixinTypes").on("/a/b").to("mix:referenceable");
graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
@@ -122,11 +126,18 @@
}
};
+ // Set up the repo type manager
+ JcrNodeTypeSource nodeTypes = null;
+ nodeTypes = new JcrBuiltinNodeTypeSource(context, nodeTypes);
+ nodeTypes = new DnaBuiltinNodeTypeSource(context, nodeTypes);
+ repoTypeManager = new RepositoryNodeTypeManager(context, nodeTypes);
+
// Stub out the repository, since we only need a few methods ...
MockitoAnnotations.initMocks(this);
stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
stub(repository.getConnectionFactory()).toReturn(connectionFactory);
-
+ stub(repository.getRepositoryTypeManager()).toReturn(repoTypeManager);
+
// Set up the session attributes ...
sessionAttributes = new HashMap<String, Object>();
sessionAttributes.put("attribute1", "value1");
@@ -404,7 +415,10 @@
public void rootNodeShouldHaveProperType() throws Exception {
Node rootNode = session.getRootNode();
- assertTrue(rootNode.getPrimaryNodeType().equals(session.nodeTypeManager().getNodeType(DnaLexicon.ROOT)));
+ NodeType rootNodePrimaryType = rootNode.getPrimaryNodeType();
+ NodeType dnaRootType = session.nodeTypeManager().getNodeType(DnaLexicon.ROOT);
+
+ assertThat(rootNodePrimaryType.getName(), is(dnaRootType.getName()));
}
@@ -447,4 +461,34 @@
}
+ /*
+ * Moved these three tests over from AbstractJcrNode as they require more extensive scaffolding that is already implemented in
+ * this test.
+ */
+
+ @Test
+ public void shouldProvideUuidIfReferenceable() throws Exception {
+ // The root node is referenceable in DNA
+ Node rootNode = session.getRootNode();
+
+ UUID uuid = ((AbstractJcrNode)rootNode).internalUuid();
+ assertThat(rootNode.getUUID(), is(uuid.toString()));
+ }
+
+ @Test( expected = UnsupportedRepositoryOperationException.class )
+ public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
+ // The b node was not set up to be referenceable in this test, but does have a mixin type
+ Node node = session.getRootNode().getNode("a");
+
+ node.getUUID();
+ }
+
+ @Test( expected = UnsupportedRepositoryOperationException.class )
+ public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
+ // The c node was not set up to be referenceable in this test and has no mixin types
+ Node node = session.getRootNode().getNode("a").getNode("b").getNode("c");
+
+ node.getUUID();
+ }
+
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrValueTest.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -38,6 +38,8 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
/**
* @author jverhaeg
@@ -46,11 +48,15 @@
private ValueFactories factories;
private JcrValue value;
+ @Mock
+ private SessionCache cache;
@Before
public void before() {
+ MockitoAnnotations.initMocks(this);
+
factories = new StandardValueFactories(Mockito.mock(NamespaceRegistry.class));
- value = new JcrValue(factories, PropertyType.BOOLEAN, true);
+ value = new JcrValue(factories, cache, PropertyType.BOOLEAN, true);
}
@Test
@@ -102,30 +108,30 @@
@Test( expected = ValueFormatException.class )
public void shouldNotProvideBooleanForDate() throws Exception {
- new JcrValue(factories, PropertyType.DATE, new Date()).getBoolean();
+ new JcrValue(factories, cache, PropertyType.DATE, new Date()).getBoolean();
}
@Test
public void shouldProvideDateForDate() throws Exception {
Date date = new Date();
- assertThat(new JcrValue(factories, PropertyType.DATE, date).getDate().getTime(), is(date));
+ assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getDate().getTime(), is(date));
}
@Test
public void shouldProvideDoubleForDate() throws Exception {
Date date = new Date();
- assertThat(new JcrValue(factories, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
+ assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getDouble(), is((double)date.getTime()));
}
@Test
public void shouldProvideLongForDate() throws Exception {
Date date = new Date();
- assertThat(new JcrValue(factories, PropertyType.DATE, date).getLong(), is(date.getTime()));
+ assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getLong(), is(date.getTime()));
}
@Test
public void shouldProvideStreamForDate() throws Exception {
- testProvidesStream(new JcrValue(factories, PropertyType.DATE, new Date()));
+ testProvidesStream(new JcrValue(factories, cache, PropertyType.DATE, new Date()));
}
@Test
@@ -134,158 +140,160 @@
date.set(2008, 7, 18, 12, 0, 0);
date.set(Calendar.MILLISECOND, 0);
String expectedValue = "2008-08-18T12:00:00.000";
- assertThat(new JcrValue(factories, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
+ assertThat(new JcrValue(factories, cache, PropertyType.DATE, date).getString().substring(0, expectedValue.length()),
is(expectedValue));
- assertThat(new JcrValue(factories, PropertyType.DATE, date.getTime()).getString().substring(0, expectedValue.length()),
+ assertThat(new JcrValue(factories, cache, PropertyType.DATE, date.getTime()).getString().substring(0,
+ expectedValue.length()),
is(expectedValue));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideBooleanForDouble() throws Exception {
- new JcrValue(factories, PropertyType.DOUBLE, 0.0).getBoolean();
+ new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getBoolean();
}
@Test
public void shouldProvideDateForDouble() throws Exception {
Calendar expectedValue = Calendar.getInstance();
expectedValue.setTime(new Date(0L));
- assertThat(new JcrValue(factories, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 0.0).getDate(), is(expectedValue));
}
@Test
public void shouldProvideDoubleForDouble() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.2).getDouble(), is(1.2));
}
@Test
public void shouldProvideLongForDouble() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
- assertThat(new JcrValue(factories, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0).getLong(), is(1L));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, Double.MAX_VALUE).getLong(), is(Long.MAX_VALUE));
}
@Test
public void shouldProvideStreamForDouble() throws Exception {
- testProvidesStream(new JcrValue(factories, PropertyType.DOUBLE, 1.0));
+ testProvidesStream(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0));
}
@Test
public void shouldProvideStringForDouble() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
+ assertThat(new JcrValue(factories, cache, PropertyType.DOUBLE, 1.0).getString(), is("1.0"));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideBooleanForLong() throws Exception {
- new JcrValue(factories, PropertyType.LONG, 0L).getBoolean();
+ new JcrValue(factories, cache, PropertyType.LONG, 0L).getBoolean();
}
@Test
public void shouldProvideDateForLong() throws Exception {
Calendar expectedValue = Calendar.getInstance();
expectedValue.setTime(new Date(0L));
- assertThat(new JcrValue(factories, PropertyType.LONG, 0L).getDate(), is(expectedValue));
+ assertThat(new JcrValue(factories, cache, PropertyType.LONG, 0L).getDate(), is(expectedValue));
}
@Test
public void shouldProvideDoubleForLong() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getDouble(), is(1.0));
+ assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getDouble(), is(1.0));
}
@Test
public void shouldProvideLongForLong() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getLong(), is(1L));
+ assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getLong(), is(1L));
}
@Test
public void shouldProvideStreamForLong() throws Exception {
- testProvidesStream(new JcrValue(factories, PropertyType.LONG, 1L));
+ testProvidesStream(new JcrValue(factories, cache, PropertyType.LONG, 1L));
}
@Test
public void shouldProvideStringForLong() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.LONG, 1L).getString(), is("1"));
+ assertThat(new JcrValue(factories, cache, PropertyType.LONG, 1L).getString(), is("1"));
}
@Test
public void shouldProvideBooleanForString() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "true").getBoolean(), is(true));
- assertThat(new JcrValue(factories, PropertyType.STRING, "yes").getBoolean(), is(false));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "true").getBoolean(), is(true));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "yes").getBoolean(), is(false));
}
@Test
public void shouldProvideDateForString() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "2008").getDate(), notNullValue());
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "2008").getDate(), notNullValue());
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDateForInvalidString() throws Exception {
- new JcrValue(factories, PropertyType.STRING, "true").getDate();
+ new JcrValue(factories, cache, PropertyType.STRING, "true").getDate();
}
@Test
public void shouldProvideDoubleForString() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "1").getDouble(), is(1.0));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "1").getDouble(), is(1.0));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDoubleForInvalidString() throws Exception {
- new JcrValue(factories, PropertyType.STRING, "true").getDouble();
+ new JcrValue(factories, cache, PropertyType.STRING, "true").getDouble();
}
@Test
public void shouldProvideLongForString() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "1").getLong(), is(1L));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "1").getLong(), is(1L));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideLongForInvalidString() throws Exception {
- new JcrValue(factories, PropertyType.STRING, "true").getLong();
+ new JcrValue(factories, cache, PropertyType.STRING, "true").getLong();
}
@Test
public void shouldProvideStreamForString() throws Exception {
- testProvidesStream(new JcrValue(factories, PropertyType.STRING, "true"));
+ testProvidesStream(new JcrValue(factories, cache, PropertyType.STRING, "true"));
}
@Test
public void shouldProvideStringForString() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "true").getString(), is("true"));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "true").getString(), is("true"));
}
@Test
public void shouldProvideBooleanForUuid() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getBoolean(), is(false));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDateForUuid() throws Exception {
- new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDate();
+ new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getDate();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDoubleForUuid() throws Exception {
- new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getDouble();
+ new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getDouble();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideLongForUuid() throws Exception {
- new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()).getLong();
+ new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()).getLong();
}
@Test
public void shouldProvideStreamForUuid() throws Exception {
- testProvidesStream(new JcrValue(factories, PropertyType.STRING, UUID.randomUUID()));
+ testProvidesStream(new JcrValue(factories, cache, PropertyType.STRING, UUID.randomUUID()));
}
@Test
public void shouldProvideStringForUuid() throws Exception {
String expectedValue = "40d373f7-75ad-4d84-900e-c72ebd98abb9";
- assertThat(new JcrValue(factories, PropertyType.STRING, UUID.fromString(expectedValue)).getString(), is(expectedValue));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, UUID.fromString(expectedValue)).getString(),
+ is(expectedValue));
}
@Test
public void shouldProvideLength() throws Exception {
- assertThat(new JcrValue(factories, PropertyType.STRING, "test").getLength(), is(4L));
- assertThat(new JcrValue(factories, PropertyType.BINARY, "test").getLength(), is(4L));
+ assertThat(new JcrValue(factories, cache, PropertyType.STRING, "test").getLength(), is(4L));
+ assertThat(new JcrValue(factories, cache, PropertyType.BINARY, "test").getLength(), is(4L));
}
private void testProvidesStream( JcrValue value ) throws Exception {
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -71,6 +71,7 @@
public class SessionCacheTest {
private ExecutionContext context;
+ private RepositoryNodeTypeManager repoTypes;
private JcrNodeTypeManager nodeTypes;
private Stopwatch sw;
private Graph store;
@@ -95,10 +96,12 @@
stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
// Load up all the node types ...
- JcrNodeTypeSource nodeTypeSource = new JcrBuiltinNodeTypeSource(session);
- nodeTypeSource = new DnaBuiltinNodeTypeSource(session, nodeTypeSource);
- nodeTypeSource = new VehixNodeTypeSource(session, nodeTypeSource);
- nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
+ JcrNodeTypeSource nodeTypeSource = null;
+ nodeTypeSource = new JcrBuiltinNodeTypeSource(this.context, nodeTypeSource);
+ nodeTypeSource = new DnaBuiltinNodeTypeSource(this.context, nodeTypeSource);
+ nodeTypeSource = new VehixNodeTypeSource(context, nodeTypeSource);
+ repoTypes = new RepositoryNodeTypeManager(context, nodeTypeSource);
+ nodeTypes = new JcrNodeTypeManager(this.context, repoTypes);
stub(session.nodeTypeManager()).toReturn(nodeTypes);
// Now set up the graph and session cache ...
@@ -113,24 +116,24 @@
private final List<JcrNodeType> primaryNodeTypes;
private final List<JcrNodeType> mixinNodeTypes;
- public VehixNodeTypeSource( JcrSession session,
+ public VehixNodeTypeSource( ExecutionContext context,
JcrNodeTypeSource predecessor ) {
super(predecessor);
this.primaryNodeTypes = new ArrayList<JcrNodeType>();
this.mixinNodeTypes = new ArrayList<JcrNodeType>();
- Name carName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:car");
- Name aircraftName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:aircraft");
+ Name carName = context.getValueFactories().getNameFactory().create("vehix:car");
+ Name aircraftName = context.getValueFactories().getNameFactory().create("vehix:aircraft");
JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
// Add in the "vehix:car" node type (which extends "nt:unstructured") ...
- JcrNodeType car = new JcrNodeType(session, carName, Arrays.asList(new NodeType[] {unstructured}),
- NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
- ORDERABLE_CHILD_NODES);
+ JcrNodeType car = new JcrNodeType(context, (RepositoryNodeTypeManager)null, carName,
+ Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES,
+ NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
// Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
- JcrNodeType aircraft = new JcrNodeType(session, aircraftName, Arrays.asList(new NodeType[] {unstructured}),
- NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
- ORDERABLE_CHILD_NODES);
+ JcrNodeType aircraft = new JcrNodeType(context, (RepositoryNodeTypeManager)null, aircraftName,
+ Arrays.asList(new NodeType[] {unstructured}), NO_PRIMARY_ITEM_NAME,
+ NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN, ORDERABLE_CHILD_NODES);
primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java 2009-03-20 18:22:45 UTC (rev 784)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java 2009-03-20 18:40:19 UTC (rev 785)
@@ -29,6 +29,7 @@
import java.util.List;
import javax.jcr.PropertyType;
import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.graph.ExecutionContext;
/**
* Node type source with additional node types that can be used for testing. This class defines its own namespace for its types.
@@ -40,7 +41,7 @@
/** The list of mixin node types. */
private final List<JcrNodeType> mixinNodeTypes;
- TestNodeTypeSource( JcrSession session,
+ TestNodeTypeSource( ExecutionContext context,
JcrNodeTypeSource predecessor ) {
super(predecessor);
@@ -50,32 +51,35 @@
JcrNodeType base = findType(JcrNtLexicon.BASE);
if (base == null) {
- String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
- String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+ String baseTypeName = JcrNtLexicon.BASE.getString(context.getNamespaceRegistry());
+ String namespaceTypeName = DnaLexicon.NAMESPACE.getString(context.getNamespaceRegistry());
throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
}
+
+
// Stubbing in child node and property definitions for now
JcrNodeType constrainedType = new JcrNodeType(
- session,
+ context,
+ NO_NODE_TYPE_MANAGER,
TestLexicon.CONSTRAINED_TYPE,
Arrays.asList(new NodeType[] {base}),
NO_PRIMARY_ITEM_NAME,
NO_CHILD_NODES,
Arrays.asList(new JcrPropertyDefinition[] {
- new JcrPropertyDefinition(session, null,
+ new JcrPropertyDefinition(context, null,
TestLexicon.CONSTRAINED_BINARY,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.BINARY, new String[] {"[,5)",
"[10, 20)", "(30,40]", "[50,]"}, false),
- new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_DATE,
+ new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_DATE,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.DATE, new String[] {
"[,+1945-08-01T01:30:00.000Z]",
"[+1975-08-01T01:30:00.000Z,)"}, false),
- new JcrPropertyDefinition(session, null,
+ new JcrPropertyDefinition(context, null,
TestLexicon.CONSTRAINED_DOUBLE,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
@@ -83,33 +87,33 @@
new String[] {"[,5.0)", "[10.1, 20.2)",
"(30.3,40.4]", "[50.5,]"}, false),
- new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_LONG,
+ new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_LONG,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.LONG, new String[] {"[,5)",
"[10, 20)", "(30,40]", "[50,]"}, false),
- new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_NAME,
+ new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_NAME,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.NAME, new String[] {
"jcr:system", "dnatest:constrainedType"},
false),
- new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_PATH,
+ new JcrPropertyDefinition(context, null, TestLexicon.CONSTRAINED_PATH,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.PATH, new String[] {
// "/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"}, false),
"/jcr:system/*", "b", "/a/b/c"}, false),
- new JcrPropertyDefinition(session, null,
+ new JcrPropertyDefinition(context, null,
TestLexicon.CONSTRAINED_REFERENCE,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
PropertyType.REFERENCE,
new String[] {"dna:root",}, false),
- new JcrPropertyDefinition(session, null,
+ new JcrPropertyDefinition(context, null,
TestLexicon.CONSTRAINED_STRING,
OnParentVersionBehavior.IGNORE.getJcrValue(),
false, false, false, NO_DEFAULT_VALUES,
15 years, 1 month
DNA SVN: r784 - in trunk: dna-jcr/src/main/java/org/jboss/dna/jcr and 4 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-20 14:22:45 -0400 (Fri, 20 Mar 2009)
New Revision: 784
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
Log:
DNA-194 Implement update JCR capability
Added most of the infrastructure to allow updates to the SessionCache. The cached representations of the nodes are changed and corresponding requests are accumulated in a Graph.Batch that are sent to the repository store when SessionCache.save() is called. This implementation hooks some of the infrastructure up to the JcrAbstractNode implementation, although more tests need to be written to verify the behavior.
Currently, only deleting nodes has been tested and is thought to be complete. Changing properties, creating children, and moving nodes are partially implemented. (Some changes to the node type manager are required, but there is currently an ongoing refactoring of JcrNodeTypeManager. This is the primary reason for committing this partially-complete work.)
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -54,6 +54,7 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.PropertyFactory;
import org.jboss.dna.graph.property.Reference;
@@ -66,6 +67,7 @@
import org.jboss.dna.graph.request.CreateWorkspaceRequest;
import org.jboss.dna.graph.request.DeleteBranchRequest;
import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
import org.jboss.dna.graph.request.InvalidWorkspaceException;
import org.jboss.dna.graph.request.MoveBranchRequest;
import org.jboss.dna.graph.request.ReadAllChildrenRequest;
@@ -77,6 +79,7 @@
import org.jboss.dna.graph.request.ReadPropertyRequest;
import org.jboss.dna.graph.request.RemovePropertiesRequest;
import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
import org.jboss.dna.graph.request.UpdatePropertiesRequest;
import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest.CloneConflictBehavior;
@@ -217,6 +220,12 @@
* </p>
*
* @param request the request to be executed (may be a {@link CompositeRequest}.
+ * @throws PathNotFoundException if the request used a node that did not exist
+ * @throws InvalidRequestException if the request was not valid
+ * @throws InvalidWorkspaceException if the workspace used in the request was not valid
+ * @throws UnsupportedRequestException if the request was not supported by the source
+ * @throws RepositorySourceException if an error occurs during execution
+ * @throws RuntimeException if a runtime error occurs during execution
*/
protected void execute( Request request ) {
RepositoryConnection connection = Graph.this.getConnectionFactory().createConnection(getSourceName());
@@ -1948,6 +1957,24 @@
}
/**
+ * Return whether this batch has been {@link #execute() executed}.
+ *
+ * @return true if this batch has already been executed, or false otherwise
+ */
+ public boolean hasExecuted() {
+ return executed;
+ }
+
+ /**
+ * Determine whether this batch needs to be executed (there are requests and the batch has not been executed yet).
+ *
+ * @return true if there are some requests in this batch that need to be executed, or false execution is not required
+ */
+ public boolean isExecuteRequired() {
+ return !executed || requestQueue.size() != 0;
+ }
+
+ /**
* Obtain the graph that this batch uses.
*
* @return the graph; never null
@@ -3318,6 +3345,11 @@
};
}
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.Graph.Executable#execute()
+ */
public Results execute() {
return this.requestQueue.execute();
}
@@ -3406,6 +3438,12 @@
* Stop accumulating the requests, submit them to the repository source, and return the results.
*
* @return the results containing the requested information from the repository.
+ * @throws PathNotFoundException if a request used a node that did not exist
+ * @throws InvalidRequestException if a request was not valid
+ * @throws InvalidWorkspaceException if the workspace used in a request was not valid
+ * @throws UnsupportedRequestException if a request was not supported by the source
+ * @throws RepositorySourceException if an error occurs during execution
+ * @throws RuntimeException if a runtime error occurs during execution
*/
Results execute();
}
@@ -4539,7 +4577,15 @@
* @param nodeName the name of the new node
* @return the interface used to complete the request
*/
- CreateAction<Next> nodeNamed( String nodeName );
+ Create<Next> nodeNamed( String nodeName );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @return the interface used to complete the request
+ */
+ Create<Next> nodeNamed( Name nodeName );
}
/**
@@ -4653,6 +4699,7 @@
void submit( Request request );
void submit( List<Request> requests );
+
}
/**
@@ -4700,6 +4747,15 @@
return this.requests;
}
+ /**
+ * Determine the number of requests that are currently in the queue.
+ *
+ * @return the number of currently-enqueued requests
+ */
+ public int size() {
+ return requests.size();
+ }
+
public void submit( Request request ) {
if (!requests.isEmpty() && request instanceof UpdatePropertiesRequest) {
// If the previous request was also an update, then maybe they can be merged ...
@@ -5593,6 +5649,10 @@
Name nameObj = factory.create(name);
return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, nameObj);
}
+
+ public CreateAction<T> nodeNamed( Name name ) {
+ return new CreateAction<T>(afterConjunction(), queue(), parent, workspaceName, name);
+ }
}
@Immutable
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -166,33 +166,4 @@
return path().size();
}
- /**
- * {@inheritDoc}
- *
- * @throws UnsupportedOperationException always
- * @see javax.jcr.Item#refresh(boolean)
- */
- public void refresh( boolean keepChanges ) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws UnsupportedOperationException always
- * @see javax.jcr.Item#remove()
- */
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws UnsupportedOperationException always
- * @see javax.jcr.Item#save()
- */
- public void save() {
- throw new UnsupportedOperationException();
- }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -990,4 +990,24 @@
}
return patterns;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Item#refresh(boolean)
+ */
+ public void refresh( boolean keepChanges ) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Item#save()
+ */
+ public void save() {
+ throw new UnsupportedOperationException();
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -264,4 +264,34 @@
public final void setValue( Node value ) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Item#refresh(boolean)
+ */
+ public void refresh( boolean keepChanges ) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Item#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Item#save()
+ */
+ public void save() {
+ throw new UnsupportedOperationException();
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -76,6 +76,10 @@
public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
public static I18n missingNodeTypeForExistingNode;
+ public static I18n unableToRemoveRootNode;
+ public static I18n unableToMoveNodeToBeChildOfDecendent;
+ public static I18n nodeHasAlreadyBeenRemovedFromThisSession;
+
public static I18n typeNotFound;
public static I18n supertypeNotFound;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -24,6 +24,7 @@
package org.jboss.dna.jcr;
import java.util.UUID;
+import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
@@ -85,4 +86,23 @@
public String getPath() throws RepositoryException {
return cache.getPathFor(nodeUuid).getString(namespaces());
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.jcr.Item#remove()
+ */
+ public void remove() throws RepositoryException {
+ try {
+ SessionCache.NodeEditor editor = cache.getEditorFor(nodeInfo().getParent());
+ editor.destroyChild(nodeUuid);
+ } catch (ItemNotFoundException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+ throw new RepositoryException(msg);
+ } catch (InvalidItemStateException err) {
+ String msg = JcrI18n.nodeHasAlreadyBeenRemovedFromThisSession.text(nodeUuid, cache.workspaceName());
+ throw new RepositoryException(msg);
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -28,6 +28,7 @@
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
import net.jcip.annotations.NotThreadSafe;
/**
@@ -115,4 +116,15 @@
}
throw new ItemNotFoundException(JcrI18n.tooDeep.text(depth));
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.jcr.Item#remove()
+ */
+ public void remove() throws ConstraintViolationException {
+ String msg = JcrI18n.unableToRemoveRootNode.text(cache.workspaceName());
+ throw new ConstraintViolationException(msg);
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -633,10 +633,9 @@
/**
* {@inheritDoc}
*
- * @throws UnsupportedOperationException always
* @see javax.jcr.Session#save()
*/
- public void save() {
- throw new UnsupportedOperationException();
+ public void save() throws RepositoryException {
+ cache.save();
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -27,12 +27,15 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.Set;
import java.util.UUID;
+import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
@@ -40,6 +43,7 @@
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
@@ -60,6 +64,7 @@
import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.jcr.cache.ChangedNodeInfo;
import org.jboss.dna.jcr.cache.ChildNode;
import org.jboss.dna.jcr.cache.Children;
import org.jboss.dna.jcr.cache.EmptyChildren;
@@ -119,22 +124,26 @@
private final String workspaceName;
protected final ExecutionContext context;
protected final PathFactory pathFactory;
- private final NameFactory nameFactory;
- private final ValueFactory<String> stringFactory;
- private final NamespaceRegistry namespaces;
- private final PropertyFactory propertyFactory;
+ protected final NameFactory nameFactory;
+ protected final ValueFactory<String> stringFactory;
+ protected final NamespaceRegistry namespaces;
+ protected final PropertyFactory propertyFactory;
private final Graph store;
private final Name defaultPrimaryTypeName;
private final Property defaultPrimaryTypeProperty;
- private final Path rootPath;
+ protected final Path rootPath;
+ protected final Name residualName;
private UUID root;
private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
- private final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
- private final HashMap<UUID, NodeInfo> changedNodes;
+ protected final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
+ protected final HashMap<UUID, ChangedNodeInfo> changedNodes;
+ protected final HashMap<UUID, NodeInfo> deletedNodes;
+ private Graph.Batch operations;
+
public SessionCache( JcrSession session,
String workspaceName,
ExecutionContext context,
@@ -156,12 +165,17 @@
this.defaultPrimaryTypeName = JcrNtLexicon.UNSTRUCTURED;
this.defaultPrimaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, this.defaultPrimaryTypeName);
this.rootPath = pathFactory.createRootPath();
+ this.residualName = nameFactory.create(JcrNodeType.RESIDUAL_ITEM_NAME);
this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
this.cachedNodes = new HashMap<UUID, ImmutableNodeInfo>();
- this.changedNodes = new HashMap<UUID, NodeInfo>();
+ this.changedNodes = new HashMap<UUID, ChangedNodeInfo>();
+ this.deletedNodes = new HashMap<UUID, NodeInfo>();
+
+ // Create the batch operations ...
+ this.operations = this.store.batch();
}
JcrSession session() {
@@ -180,6 +194,39 @@
return session.nodeTypeManager();
}
+ final Graph.Batch operations() {
+ return operations;
+ }
+
+ /**
+ * Save any changes that have been accumulated by this session.
+ *
+ * @throws RepositoryException if any error resulting while saving the changes to the repository
+ */
+ public void save() throws RepositoryException {
+ if (operations.isExecuteRequired()) {
+ // Execute the batched operations ...
+ try {
+ operations.execute();
+ } catch (RuntimeException e) {
+ throw new RepositoryException(e);
+ }
+
+ // Create a new batch for future operations ...
+ operations = store.batch();
+ // Remove all the cached items that have been changed or deleted ...
+ for (UUID changedUuid : changedNodes.keySet()) {
+ cachedNodes.remove(changedUuid);
+ }
+ for (UUID changedUuid : deletedNodes.keySet()) {
+ cachedNodes.remove(changedUuid);
+ }
+ // Remove all the changed and deleted infos ...
+ changedNodes.clear();
+ deletedNodes.clear();
+ }
+ }
+
public JcrRootNode findJcrRootNode() throws RepositoryException {
return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
}
@@ -215,11 +262,13 @@
* @return the information for the referenced node; never null
* @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
* @throws PathNotFoundException if the node given by the relative path does not exist
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
* @throws RepositoryException if any other error occurs while reading information from the repository
* @see #findNodeInfoForRoot()
*/
public AbstractJcrNode findJcrNode( UUID uuidOfReferenceNode,
- Path relativePath ) throws PathNotFoundException, RepositoryException {
+ Path relativePath )
+ throws PathNotFoundException, InvalidItemStateException, RepositoryException {
// An existing JCR Node object was not found, so we'll have to create it by finding the underlying
// NodeInfo for the node (from the changed state or the cache) ...
NodeInfo info = findNodeInfo(uuidOfReferenceNode, relativePath);
@@ -268,11 +317,13 @@
* @return the information for the referenced item; never null
* @throws ItemNotFoundException if the reference node with the supplied UUID does not exist, or if an item given by the
* supplied relative path does not exist
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
* @throws RepositoryException if any other error occurs while reading information from the repository
* @see #findNodeInfoForRoot()
*/
public AbstractJcrItem findJcrItem( UUID uuidOfReferenceNode,
- Path relativePath ) throws ItemNotFoundException, RepositoryException {
+ Path relativePath )
+ throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
// A pathological case is an empty relative path ...
if (relativePath.size() == 0) {
return findJcrNode(uuidOfReferenceNode);
@@ -332,6 +383,412 @@
}
/**
+ * Obtain an {@link NodeEditor editor} that can be used to manipulate the properties or children on the node identified by the
+ * supplied UUID. The node must exist prior to this call, either as a node that exists in the workspace or as a node that was
+ * created within this session but not yet persited to the workspace.
+ *
+ * @param uuid the UUID of the node that is to be changed; may not be null and must represent an <i>existing</i> node
+ * @return the editor; never null
+ * @throws ItemNotFoundException if no such node could be found in the session or workspace
+ * @throws InvalidItemStateException if the item has been marked for deletion within this session
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ */
+ public NodeEditor getEditorFor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+ // See if we already have something in the changed nodes ...
+ ChangedNodeInfo info = changedNodes.get(uuid);
+ Location currentLocation = null;
+ if (info == null) {
+ // Or in the cache ...
+ NodeInfo cached = cachedNodes.get(uuid);
+ if (cached == null) {
+ cached = loadFromGraph(uuid, null);
+ }
+ // Now put into the changed nodes ...
+ info = new ChangedNodeInfo(cached);
+ changedNodes.put(uuid, info);
+ currentLocation = info.getOriginalLocation();
+ } else {
+ // compute the current location ...
+ currentLocation = Location.create(getPathFor(info), uuid);
+ }
+ return new NodeEditor(info, currentLocation);
+ }
+
+ /**
+ * An interface used to manipulate a node's properties and children.
+ */
+ public final class NodeEditor {
+ private final ChangedNodeInfo node;
+ private final Location currentLocation;
+
+ protected NodeEditor( ChangedNodeInfo node,
+ Location currentLocation ) {
+ this.node = node;
+ this.currentLocation = currentLocation;
+ }
+
+ /**
+ * Set the value for the property. If the property does not exist, it will be added. If the property does exist, the
+ * existing values will be replaced with the supplied value.
+ *
+ * @param name the property name; may not be null
+ * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
+ * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
+ * definition constraint
+ */
+ public void setProperty( Name name,
+ Object value ) throws ConstraintViolationException {
+ Property dnaProp = propertyFactory.create(name, value);
+ setProperty(name, dnaProp);
+ }
+
+ /**
+ * Set the values for the property. If the property does not exist, it will be added. If the property does exist, the
+ * existing values will be replaced with those that are supplied.
+ *
+ * @param name the property name; may not be null
+ * @param value the new property values, which may be converted to the appropriate {@link PropertyType type}
+ * @throws ConstraintViolationException if the property could not be set because of a node type constraint or property
+ * definition constraint
+ */
+ public void setProperty( Name name,
+ Object[] value ) throws ConstraintViolationException {
+ Property dnaProp = propertyFactory.create(name, value);
+ setProperty(name, dnaProp);
+ }
+
+ protected final void setProperty( Name name,
+ Property dnaProp ) throws ConstraintViolationException {
+ PropertyInfo existing = node.getProperty(name);
+ PropertyInfo newProperty = null;
+ if (existing != null) {
+ // We're replacing an existing property, but we still need to check that the property definition
+ // defines a type. So, find the property definition for the existing property ...
+ JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(existing.getDefinitionId(),
+ existing.isMultiValued());
+
+ // Look at the required property type ...
+ int propertyType = definition.getRequiredType();
+ if (propertyType == PropertyType.UNDEFINED) {
+ // We need set the new type to that defined by the values ...
+ propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+ }
+
+ // Csreate the property info ...
+ newProperty = new PropertyInfo(existing.getPropertyId(), existing.getDefinitionId(), propertyType, dnaProp,
+ existing.isMultiValued());
+ } else {
+ // It's a new property ...
+ PropertyId id = new PropertyId(node.getUuid(), name);
+ // Look find the property definition to use ...
+ JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProp);
+
+ // Figure out the property type ...
+ int propertyType = definition.getRequiredType();
+ if (propertyType == PropertyType.UNDEFINED) {
+ propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+ }
+ // Create the property info ...
+ newProperty = new PropertyInfo(id, definition.getId(), propertyType, dnaProp, definition.isMultiple());
+ }
+ node.setProperty(newProperty, context().getValueFactories());
+ operations().set(dnaProp).on(currentLocation);
+ }
+
+ /**
+ * Remove the existing property with the supplied name.
+ *
+ * @param name the property name; may not be null
+ * @return true if there was a property with the supplied name, or false if no such property existed
+ */
+ public boolean removeProperty( Name name ) {
+ if (node.removeProperty(name) != null) {
+ operations().remove(name).on(currentLocation);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Move the child specified by the supplied UUID to be a child of this node, appending the child to the end of the current
+ * list of children. This method automatically disconnects the node from its current parent.
+ *
+ * @param nodeUuid the UUID of the existing node; may not be null
+ * @return the representation of the newly-added child, which includes the {@link ChildNode#getSnsIndex()
+ * same-name-sibling index}
+ * @throws ItemNotFoundException if the specified child node could be found in the session or workspace
+ * @throws InvalidItemStateException if the specified child has been marked for deletion within this session
+ * @throws ConstraintViolationException if moving the node into this node violates this node's definition
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ */
+ public ChildNode moveToBeChild( UUID nodeUuid )
+ throws ItemNotFoundException, InvalidItemStateException, ConstraintViolationException, RepositoryException {
+
+ if (nodeUuid.equals(node.getUuid()) || isAncestor(nodeUuid)) {
+ Path pathOfNode = getPathFor(nodeUuid);
+ Path thisPath = currentLocation.getPath();
+ String msg = JcrI18n.unableToMoveNodeToBeChildOfDecendent.text(pathOfNode, thisPath, workspaceName());
+ throw new RepositoryException(msg);
+ }
+
+ // Is the node already a child?
+ ChildNode child = node.getChildren().getChild(nodeUuid);
+ if (child != null) return child;
+
+ // Get an editor for the child (in its current location) and one for its parent ...
+ NodeEditor existingNodeEditor = getEditorFor(nodeUuid);
+ ChangedNodeInfo existingNodeInfo = existingNodeEditor.node;
+ UUID existingParent = existingNodeInfo.getParent();
+ NodeEditor existingParentEditor = getEditorFor(existingParent);
+ ChangedNodeInfo existingParentInfo = existingParentEditor.node;
+
+ // Verify that this node's definition allows the specified child ...
+ Name childName = existingParentInfo.getChildren().getChild(nodeUuid).getName();
+ int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(childName);
+ JcrNodeDefinition definition = findBestChildNodeDefinition(childName, numSns);
+ if (!definition.getId().equals(node.getDefinitionId())) {
+ // The node definition changed, so try to set the property ...
+ try {
+ setProperty(DnaLexicon.NODE_DEFINITON, definition.getId().getString());
+ } catch (ConstraintViolationException e) {
+ // We can't set this property on the node (according to the node definition).
+ // But we still want the node info to have the correct node definition.
+ // When it is reloaded into a cache (after being persisted), the correct node definition
+ // will be computed again ...
+ node.setDefinitionId(definition.getId());
+
+ // And remove the property from the info ...
+ existingNodeEditor.removeProperty(DnaLexicon.NODE_DEFINITON);
+ }
+ }
+
+ // Remove the node from the current parent and add it to this ...
+ child = existingParentInfo.removeChild(nodeUuid, pathFactory);
+ ChildNode newChild = node.addChild(child.getName(), child.getUuid(), pathFactory);
+
+ // Set the child's changed representation to point to this node as its parent ...
+ existingNodeInfo.setParent(node.getUuid());
+
+ // Now, record the operation to do this ...
+ operations().move(existingNodeEditor.currentLocation).into(currentLocation);
+
+ return newChild;
+ }
+
+ /**
+ * Create a new node as a child of this node, using the supplied name and (optionally) the supplied UUID.
+ *
+ * @param name the name for the new child; may not be null
+ * @param desiredUuid the desired UUID, or null if the UUID for the child should be generated automatically
+ * @param primaryTypeName the name of the primary type for the new node
+ * @param nodeDefinitionId
+ * @return the representation of the newly-created child, which includes the {@link ChildNode#getSnsIndex()
+ * same-name-sibling index}
+ * @throws InvalidItemStateException if the specified child has been marked for deletion within this session
+ * @throws ConstraintViolationException if moving the node into this node violates this node's definition
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ */
+ public ChildNode createChild( Name name,
+ UUID desiredUuid,
+ Name primaryTypeName,
+ NodeDefinitionId nodeDefinitionId )
+ throws InvalidItemStateException, ConstraintViolationException, RepositoryException {
+ if (desiredUuid == null) desiredUuid = UUID.randomUUID();
+
+ // Verify that this node accepts a child of the supplied name (given any existing SNS nodes) ...
+ int numSns = node.getChildren().getCountOfSameNameSiblingsWithName(name);
+ JcrNodeDefinition definition = findBestChildNodeDefinition(name, numSns);
+
+ ChildNode result = node.addChild(name, desiredUuid, pathFactory);
+
+ // ---------------------------------------------------------
+ // Now create the child node representation in the cache ...
+ // ---------------------------------------------------------
+ Path newPath = pathFactory.create(currentLocation.getPath(), result.getSegment());
+ Location location = Location.create(newPath, desiredUuid);
+
+ // Create the properties ...
+ Map<Name, PropertyInfo> properties = new HashMap<Name, PropertyInfo>();
+ Property primaryTypeProp = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
+ Property nodeDefinitionProp = propertyFactory.create(DnaLexicon.NODE_DEFINITON, nodeDefinitionId.getString());
+
+ // Create the property info for the "jcr:primaryType" child property ...
+ JcrPropertyDefinition primaryTypeDefn = findBestPropertyDefintion(primaryTypeProp, primaryTypeName);
+ PropertyDefinitionId primaryTypeDefinitionId = primaryTypeDefn.getId();
+ PropertyInfo primaryTypeInfo = new PropertyInfo(new PropertyId(desiredUuid, primaryTypeProp.getName()),
+ primaryTypeDefinitionId, PropertyType.NAME, primaryTypeProp, false);
+ properties.put(primaryTypeProp.getName(), primaryTypeInfo);
+
+ // Create the property info for the "dna:nodeDefinition" child property ...
+ JcrPropertyDefinition nodeDefnDefn = findBestPropertyDefintion(nodeDefinitionProp, primaryTypeName);
+ if (nodeDefnDefn != null) {
+ PropertyDefinitionId nodeDefnDefinitionId = nodeDefnDefn.getId();
+ PropertyInfo nodeDefinitionInfo = new PropertyInfo(new PropertyId(desiredUuid, nodeDefinitionProp.getName()),
+ nodeDefnDefinitionId, PropertyType.STRING, nodeDefinitionProp,
+ true);
+ properties.put(nodeDefinitionProp.getName(), nodeDefinitionInfo);
+ }
+
+ // Now create the child node info, putting it in the changed map (and not the cache map!) ...
+ NodeInfo info = new ImmutableNodeInfo(location, primaryTypeName, null, definition.getId(), node.getUuid(), null,
+ properties);
+ ChangedNodeInfo changedInfo = new ChangedNodeInfo(info);
+ changedNodes.put(desiredUuid, changedInfo);
+
+ // ---------------------------------------
+ // Now record the changes to the store ...
+ // ---------------------------------------
+ Graph.Create<Graph.Batch> create = operations().createUnder(currentLocation)
+ .nodeNamed(name)
+ .with(desiredUuid)
+ .with(primaryTypeProp);
+ if (nodeDefnDefn != null) {
+ create = create.with(nodeDefinitionProp);
+ }
+ create.and();
+ return result;
+ }
+
+ /**
+ * Destroy the child node with the supplied UUID and all nodes that exist below it, including any nodes that were created
+ * and haven't been persisted.
+ *
+ * @param nodeUuid the UUID of the child node; may not be null
+ * @return true if the child was successfully removed, or false if the node did not exist as a child
+ */
+ public boolean destroyChild( UUID nodeUuid ) {
+ ChildNode deleted = node.removeChild(nodeUuid, pathFactory);
+
+ if (deleted != null) {
+ // Recursively mark the cached/changed information as deleted ...
+ deleteNodeInfos(nodeUuid);
+
+ // Now make the request to the source ...
+ Path childPath = pathFactory.create(currentLocation.getPath(), deleted.getSegment());
+ Location locationOfChild = Location.create(childPath, nodeUuid);
+ operations().delete(locationOfChild);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Find the best property definition in this node's
+ *
+ * @param dnaProperty the new property that is to be set on this node
+ * @return the property definition that allows setting this property; never null
+ * @throws ConstraintViolationException if setting the property would violates this node's definition or the property's
+ * definition
+ */
+ protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty ) throws ConstraintViolationException {
+ // First check the primary type ...
+ JcrPropertyDefinition definition = findBestPropertyDefintion(dnaProperty, node.getPrimaryTypeName());
+ if (definition != null) {
+ // TODO: Does this definition allow this value? ...
+ return definition;
+ }
+ // Check the mixin types ...
+ for (Name mixinTypeName : node.getMixinTypeNames()) {
+ definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
+ if (definition != null) {
+ // TODO: Does this definition allow this value? ...
+ return definition;
+ }
+ }
+
+ // Nothing was found yet, so check the residual property definitions, starting with the primary type ...
+ definition = findBestPropertyDefintion(dnaProperty, residualName);
+ if (definition != null) {
+ // TODO: Does this definition allow this value? ...
+ return definition;
+ }
+ // Check the mixin types ...
+ for (Name mixinTypeName : node.getMixinTypeNames()) {
+ definition = findBestPropertyDefintion(dnaProperty, mixinTypeName);
+ if (definition != null) {
+ // TODO: Does this definition allow this value? ...
+ return definition;
+ }
+ }
+
+ // No definition that allowed the values ...
+ throw new ConstraintViolationException();
+ }
+
+ /**
+ * Find the best property definition in the named node type.
+ *
+ * @param dnaProperty the new property that is to be set on this node
+ * @param nodeTypeName the name of the node type that should be checked; may not be null
+ * @return the property definition that allows setting this property; or null if no valid property definition could be
+ * found in the given node type
+ * @see #findBestPropertyDefintion(Property)
+ */
+ protected JcrPropertyDefinition findBestPropertyDefintion( Property dnaProperty,
+ Name nodeTypeName ) {
+ JcrNodeType nodeType = nodeTypes().getNodeType(nodeTypeName);
+ Name name = dnaProperty.getName();
+ JcrPropertyDefinition definition = null;
+ if (dnaProperty.isSingle()) {
+ // First look for a single-valued property definition with a matching name ...
+ definition = nodeType.getPropertyDefinition(name, false);
+ if (definition != null) return definition;
+
+ // Then look for a residual definition ...
+ definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, false);
+ if (definition != null) return definition;
+ }
+
+ // Either the DNA property has 0 or 2+ values (and we couldn't use a single-valued definition)
+ // OR there was 1 value and we couldn't find a single-valued definition.
+ // So, we need to look for a multi-valued property definition ...
+
+ // First look for a definition that matches by name ...
+ definition = nodeType.getPropertyDefinition(name, true);
+ if (definition != null) return definition;
+
+ // Then look for a residual definition ...
+ definition = nodeType.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
+ if (definition != null) return definition;
+
+ // Nothing found yet ...
+ if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+ // We can use the "nt:unstructured" property definitions for any property ...
+ JcrNodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
+ definition = unstructured.getPropertyDefinition(JcrNodeType.RESIDUAL_ITEM_NAME, true);
+ if (definition != null) return definition;
+ }
+
+ // No property definition could be found ...
+ return null;
+ }
+
+ /**
+ * @param childName
+ * @param numberOfExistingChildrenWithName
+ * @return the most specific node definition for the child; never null
+ * @throws ConstraintViolationException if the new child would violates this node's definition
+ */
+ protected JcrNodeDefinition findBestChildNodeDefinition( Name childName,
+ int numberOfExistingChildrenWithName )
+ throws ConstraintViolationException {
+
+ // No definition that allowed the values ...
+ throw new ConstraintViolationException();
+ }
+
+ protected boolean isAncestor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+ UUID ancestor = node.getParent();
+ while (ancestor != null) {
+ if (ancestor.equals(uuid)) return true;
+ NodeInfo info = findNodeInfo(ancestor);
+ ancestor = info.getParent();
+ }
+ return false;
+ }
+ }
+
+ /**
* Utility method that creates and caches the appropriate kind of AbstractJcrNode implementation for node given by the
* supplied information.
*
@@ -387,20 +844,18 @@
* @param uuid the UUID for the node; may not be null
* @return the information for the node with the supplied UUID, or null if the information is not in the cache
* @throws ItemNotFoundException if there is no node with the supplied UUID
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
* @throws RepositoryException if any other error occurs while reading information from the repository
* @see #findNodeInfoInCache(UUID)
* @see #findNodeInfo(UUID, Path)
* @see #findNodeInfoForRoot()
*/
- NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, RepositoryException {
- // See if we already have something in the changed nodes ...
- NodeInfo info = changedNodes.get(uuid);
+ NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
+ // See if we already have something in the cache ...
+ NodeInfo info = findNodeInfoInCache(uuid);
if (info == null) {
- // Or in the cache ...
- info = cachedNodes.get(uuid);
- if (info == null) {
- info = loadFromGraph(uuid, null);
- }
+ // Nope, so go ahead and load it ...
+ info = loadFromGraph(uuid, null);
}
return info;
}
@@ -414,13 +869,20 @@
* @see #findNodeInfo(UUID)
* @see #findNodeInfo(UUID, Path)
* @see #findNodeInfoForRoot()
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
*/
- NodeInfo findNodeInfoInCache( UUID uuid ) {
+ NodeInfo findNodeInfoInCache( UUID uuid ) throws InvalidItemStateException {
// See if we already have something in the changed nodes ...
NodeInfo info = changedNodes.get(uuid);
if (info == null) {
// Or in the cache ...
info = cachedNodes.get(uuid);
+ if (info == null) {
+ // Finally check if the node was deleted ...
+ if (deletedNodes.containsKey(uuid)) {
+ throw new InvalidItemStateException();
+ }
+ }
}
return info;
}
@@ -452,11 +914,13 @@
* @return the information for the referenced node; never null
* @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
* @throws PathNotFoundException if the node given by the relative path does not exist
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
* @throws RepositoryException if any other error occurs while reading information from the repository
* @see #findNodeInfoForRoot()
*/
NodeInfo findNodeInfo( UUID node,
- Path relativePath ) throws ItemNotFoundException, PathNotFoundException, RepositoryException {
+ Path relativePath )
+ throws ItemNotFoundException, InvalidItemStateException, PathNotFoundException, RepositoryException {
// The relative path must be normalized ...
assert relativePath.isNormalized();
@@ -583,19 +1047,21 @@
* @param propertyId the identifier for the property; may not be null
* @return the property information, or null if the node does not contain the specified property
* @throws PathNotFoundException if the node containing this property does not exist
+ * @throws InvalidItemStateException if the node with the UUID has been deleted in this session
* @throws RepositoryException if there is an error while obtaining the information
*/
- PropertyInfo findPropertyInfo( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+ PropertyInfo findPropertyInfo( PropertyId propertyId )
+ throws PathNotFoundException, InvalidItemStateException, RepositoryException {
NodeInfo info = findNodeInfo(propertyId.getNodeId());
return info.getProperty(propertyId.getPropertyName());
}
- Path getPathFor( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+ Path getPathFor( UUID uuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
if (uuid == root) return rootPath;
return getPathFor(findNodeInfo(uuid));
}
- Path getPathFor( NodeInfo info ) throws ItemNotFoundException, RepositoryException {
+ Path getPathFor( NodeInfo info ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
if (info != null && info.getUuid() == root) return rootPath;
LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
while (info != null) {
@@ -619,7 +1085,7 @@
return getPathFor(findPropertyInfo(propertyId));
}
- protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+ protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
findNodeInfoForRoot();
if (nodeUuid == root) return nameFactory.create("");
// Get the parent ...
@@ -629,7 +1095,7 @@
return child.getName();
}
- protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+ protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
findNodeInfoForRoot();
if (nodeUuid == root) return 1;
// Get the parent ...
@@ -718,7 +1184,9 @@
if (uuid != null) {
// Check for an identification property ...
uuidProperty = location.getIdProperty(JcrLexicon.UUID);
- if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+ if (uuidProperty == null) {
+ uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
+ }
}
if (uuidProperty == null) {
uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
@@ -843,9 +1311,12 @@
}
// The process the mixin types ...
org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
+ Set<Name> mixinTypeNames = null;
if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
for (Object mixinTypeValue : mixinTypesProperty) {
Name mixinTypeName = nameFactory.create(mixinTypeValue);
+ if (mixinTypeNames == null) mixinTypeNames = new HashSet<Name>();
+ mixinTypeNames.add(mixinTypeName);
if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
String mixinTypeNameString = mixinTypeName.getString(namespaces);
NodeType mixinType = nodeTypes().getNodeType(mixinTypeNameString);
@@ -958,7 +1429,7 @@
List<Location> locations = graphNode.getChildren();
Children children = locations.isEmpty() ? new EmptyChildren(parentUuid) : new ImmutableChildren(parentUuid, locations);
props = Collections.unmodifiableMap(props);
- return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
+ return new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definition.getId(), parentUuid, children, props);
}
/**
@@ -985,4 +1456,73 @@
// TODO: should this also check the mixins?
return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
}
+
+ /**
+ * This method finds the {@link NodeInfo} for the node with the supplied UUID and marks it as being deleted, and does the same
+ * for all decendants (e.g., children, grandchildren, great-grandchildren, etc.) that have been cached or changed.
+ * <p>
+ * Note that this method only processes those nodes that are actually represented in this cache. Any branches that are not
+ * loaded are not processed. This is an acceptable assumption, since all ancestors of a cached node should also be cached.
+ * </p>
+ * <p>
+ * Also be aware that the returned count of deleted node info representations will only reflect the total number of nodes in
+ * the branch if and only if all branch nodes were cached. In all other cases, the count returned will be fewer than the
+ * number of actual nodes in the branch.
+ * </p>
+ *
+ * @param uuid the UUID of the node that should be marked as deleted; may not be null
+ * @return the number of node info representations that were marked as deleted
+ */
+ protected int deleteNodeInfos( UUID uuid ) {
+ Queue<UUID> nodesToDelete = new LinkedList<UUID>();
+ int numDeleted = 0;
+ nodesToDelete.add(uuid);
+ while (!nodesToDelete.isEmpty()) {
+ UUID toDelete = nodesToDelete.remove();
+ // Remove the node info from the changed map ...
+ NodeInfo info = changedNodes.remove(toDelete);
+ if (info == null) {
+ // Wasn't changed, so remove it from the cache map ...
+ info = cachedNodes.remove(toDelete);
+ }
+ // Whether or not we found an info, add it to the deleted map ...
+ this.deletedNodes.put(toDelete, info);
+
+ if (info != null) {
+ // Get all the children and add them to the queue ...
+ for (ChildNode child : info.getChildren()) {
+ nodesToDelete.add(child.getUuid());
+ }
+ }
+ ++numDeleted;
+ }
+ return numDeleted;
+ }
+
+ /**
+ * Find the best {@link JcrNodeDefinition child definition} for a child with the specified name given the named primary type
+ * and mixin types.
+ *
+ * @param childName the name of the child that is to be added
+ * @param childPrimaryTypeName the name of the child's primary type, or null if the child's primary type is not known
+ * @param requiresMultipleSns true if there is at least one existing child with the same name, requiring the child node type
+ * to allow same-name-siblings, or false if the child will be the first child with the supplied name
+ * @param primaryTypeName the name of the primary type for the parent; may not be null
+ * @param mixinTypeNames the names of the mixin types for the parent; may be null or empty if the parent has no mixins
+ * @return the child node definition that can be used (which may be a residual definition), or null if there is no such node
+ * type
+ */
+ protected JcrNodeDefinition findBestChildDefinition( Name childName,
+ Name childPrimaryTypeName,
+ boolean requiresMultipleSns,
+ Name primaryTypeName,
+ Collection<Name> mixinTypeNames ) {
+ // First check the primary type for a child definition with the supplied name ...
+ JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+ JcrNodeDefinition definition = primaryType.findBestNodeDefinitionForChild(childName, childPrimaryTypeName);
+ if (definition != null) {
+ // definition.
+ }
+ return null;
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -28,11 +28,15 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.JcrLexicon;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.DnaLexicon;
import org.jboss.dna.jcr.NodeDefinitionId;
/**
@@ -41,7 +45,6 @@
* Each instance maintains a reference to the original (usually immutable) NodeInfo representation that was probably read from the
* repository.
*/
-@Immutable
public class ChangedNodeInfo implements NodeInfo {
protected static final PropertyInfo DELETED_PROPERTY = null;
@@ -68,6 +71,18 @@
private Map<Name, PropertyInfo> changedProperties;
/**
+ * The updated list of mixin node type names. This is merely a cached version of what's already in the
+ * {@link JcrLexicon#MIXIN_TYPES "jcr:mixinTypes"} property.
+ */
+ private Set<Name> changedMixinTypeNames;
+
+ /**
+ * The updated node definition, which may be changed when this node is moved to a different parent (with a different node
+ * type)
+ */
+ private NodeDefinitionId changedDefinitionId;
+
+ /**
* Create an immutable NodeInfo instance.
*
* @param original the original node information, may not be null
@@ -135,13 +150,39 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
+ */
+ public Set<Name> getMixinTypeNames() {
+ if (changedMixinTypeNames != null) return changedMixinTypeNames;
+ return original.getMixinTypeNames();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
*/
public NodeDefinitionId getDefinitionId() {
+ if (changedDefinitionId != null) return changedDefinitionId;
return original.getDefinitionId();
}
/**
+ * Set the identifier of the node definition for this node. This should normally be changed by
+ * {@link #setProperty(PropertyInfo, ValueFactories) setting} the {@link DnaLexicon#NODE_DEFINITON} property. However, since
+ * that property is not always allowed, this method provides a way to set it locally (without requiring a property).
+ *
+ * @param definitionId the new property definition identifier; may not be null
+ * @see #setProperty(PropertyInfo, ValueFactories)
+ */
+ public void setDefinitionId( NodeDefinitionId definitionId ) {
+ if (!getDefinitionId().equals(definitionId)) {
+ assert definitionId != null;
+ changedDefinitionId = definitionId;
+ }
+ }
+
+ /**
* {@inheritDoc}
*
* @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
@@ -172,43 +213,42 @@
/**
* Remove a child from the children. This method only uses the child's UUID to identify the contained ChildNode instance that
* should be removed.
- * <p>
- * Note that this method returns the new {@link Children} container, which is the same as would be returned by
- * {@link #getChildren()} called immediately after this method.
- * </p>
*
* @param childUUID the UUID of the child that is to be removed; may not be null
* @param factory the path factory that should be used to create a {@link Segment} for replacement {@link ChildNode} objects
* for nodes with the same name that and higher same-name-sibiling indexes.
- * @return the Children object that has the modified children
+ * @return the child node that was removed, or null if no such child could be removed
*/
- public Children removeChild( UUID childUUID,
- PathFactory factory ) {
+ public ChildNode removeChild( UUID childUUID,
+ PathFactory factory ) {
+ ChildNode deleted = null;
if (changedChildren == null) {
// Create the changed children. First check whether there are 0 or 1 child ...
Children existing = original.getChildren();
int numExisting = existing.size();
if (numExisting == 0) {
// nothing to do, so return the original's children
- return existing;
+ return null;
}
- if (existing.getChild(childUUID) == null) {
- // The requested child doesn't exist in the children, so return silently ...
- return existing;
+ deleted = existing.getChild(childUUID);
+ if (deleted == null) {
+ // The requested child doesn't exist in the children, so return ...
+ return null;
}
if (numExisting == 1) {
// We're removing the only child in the original ...
changedChildren = new ChangedChildren(existing.getParentUuid());
- return changedChildren;
+ return existing.getChild(childUUID);
}
// There is at least one child, so create the new children container ...
assert existing instanceof InternalChildren;
InternalChildren internal = (InternalChildren)existing;
changedChildren = internal.without(childUUID, factory);
} else {
+ deleted = changedChildren.getChild(childUUID);
changedChildren = changedChildren.without(childUUID, factory);
}
- return changedChildren;
+ return deleted;
}
/**
@@ -271,8 +311,10 @@
return original.getProperty(name);
}
- public PropertyInfo setProperty( PropertyInfo newProperty ) {
+ public PropertyInfo setProperty( PropertyInfo newProperty,
+ ValueFactories factories ) {
Name name = newProperty.getPropertyName();
+ PropertyInfo previous = null;
if (changedProperties == null) {
// There were no changes made yet ...
@@ -281,18 +323,33 @@
changedProperties.put(name, newProperty);
// And return the original property (or null if there was none) ...
- return original.getProperty(name);
+ previous = original.getProperty(name);
+ } else if (changedProperties.containsKey(name)) {
+ // The property was already changed, in which case we need to return the changed one ...
+ previous = changedProperties.put(name, newProperty);
+ } else {
+ // Otherwise, the property was not yet changed or deleted ...
+ previous = original.getProperty(name);
+ changedProperties.put(name, newProperty);
}
- // The property may already have been changed, in which case we need to return the changed one ...
- if (changedProperties.containsKey(name)) {
- PropertyInfo changed = changedProperties.put(name, null);
- // The named property was indeed deleted ...
- return changed;
+ // If this property was the "jcr:mixinTypes" property, update the cached values ...
+ if (name.equals(JcrLexicon.MIXIN_TYPES)) {
+ if (changedMixinTypeNames == null) {
+ changedMixinTypeNames = new HashSet<Name>();
+ } else {
+ changedMixinTypeNames.clear();
+ }
+ NameFactory nameFactory = factories.getNameFactory();
+ for (Object value : newProperty.getProperty()) {
+ changedMixinTypeNames.add(nameFactory.create(value));
+ }
+ } else if (name.equals(DnaLexicon.NODE_DEFINITON)) {
+ ValueFactory<String> stringFactory = factories.getStringFactory();
+ String value = stringFactory.create(newProperty.getProperty().getFirstValue());
+ changedDefinitionId = NodeDefinitionId.fromString(value, factories.getNameFactory());
}
- // Otherwise, the property was not yet changed or deleted ...
- PropertyInfo changed = original.getProperty(name);
- changedProperties.put(name, newProperty);
- return changed;
+
+ return previous;
}
public PropertyInfo removeProperty( Name name ) {
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -73,4 +73,12 @@
*/
Iterator<ChildNode> getChildren( Name name );
+ /**
+ * Get the number of same-name-siblings that all share the supplied name.
+ *
+ * @param name the name for the children; may not be null
+ * @return the number of same-name-siblings with the supplied name
+ */
+ int getCountOfSameNameSiblingsWithName( Name name );
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -102,6 +102,15 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name)
+ */
+ public int getCountOfSameNameSiblingsWithName( Name name ) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
* org.jboss.dna.graph.property.PathFactory)
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -170,6 +170,15 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name)
+ */
+ public int getCountOfSameNameSiblingsWithName( Name name ) {
+ return this.childrenByName.get(name).size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
* org.jboss.dna.graph.property.PathFactory)
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.jcr.cache;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -43,19 +44,22 @@
private final NodeDefinitionId definition;
private final Children children;
private final Map<Name, PropertyInfo> properties;
+ private final Set<Name> mixinTypeNames;
/**
* Create an immutable NodeInfo instance.
*
* @param originalLocation the original location
* @param primaryTypeName the name of the node's primary type
+ * @param mixinTypeNames the names of the mixin types for this node, or null if there are none
* @param definition the definition used when creating the node
* @param parent the parent
- * @param children the immutable children
- * @param properties the unmodifiable map of properties
+ * @param children the immutable children; may be null if there are no children
+ * @param properties the unmodifiable map of properties; may be null if there are no properties
*/
public ImmutableNodeInfo( Location originalLocation,
Name primaryTypeName,
+ Set<Name> mixinTypeNames,
NodeDefinitionId definition,
UUID parent,
Children children,
@@ -65,11 +69,17 @@
this.definition = definition;
this.parent = parent;
this.uuid = this.originalLocation.getUuid();
- this.children = children;
+ this.children = children != null ? children : new EmptyChildren(this.uuid);
+ if (properties == null) properties = Collections.emptyMap();
this.properties = properties;
+ if (mixinTypeNames == null) mixinTypeNames = Collections.emptySet();
+ this.mixinTypeNames = mixinTypeNames;
assert this.uuid != null;
assert this.definition != null;
assert this.primaryTypeName != null;
+ assert this.children != null;
+ assert this.mixinTypeNames != null;
+ assert this.properties != null;
}
/**
@@ -111,6 +121,15 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getMixinTypeNames()
+ */
+ public Set<Name> getMixinTypeNames() {
+ return mixinTypeNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
*/
public NodeDefinitionId getDefinitionId() {
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -55,6 +55,13 @@
public Name getPrimaryTypeName();
/**
+ * Get the names of the mixin types for this node.
+ *
+ * @return the unmodifiable set of mixin type names; never null but possibly empty
+ */
+ public Set<Name> getMixinTypeNames();
+
+ /**
* @return definition
*/
public NodeDefinitionId getDefinitionId();
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-03-20 18:22:45 UTC (rev 784)
@@ -66,6 +66,10 @@
nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
+unableToRemoveRootNode = Unable to remove the root node in workspace "{1}"
+unableToMoveNodeToBeChildOfDecendent = Node "{0}" in workspace "{2}" cannot be moved under a decendant node ("{1}")
+nodeHasAlreadyBeenRemovedFromThisSession = Node "{0}" in workspace "{1} has already been removed from this session
+
REP_NAME_DESC = DNA Repository
REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC
SPEC_NAME_DESC = Content Repository for Java Technology API
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -72,6 +72,18 @@
public boolean isNode() {
return false;
}
+
+ public void refresh( boolean keepChanges ) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void save() {
+ throw new UnsupportedOperationException();
+ }
};
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -100,6 +100,10 @@
public String getPath() throws RepositoryException {
return cache.getPathFor(nodeInfo()).getString(namespaces());
}
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
}
private ExecutionContext context;
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -25,8 +25,11 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
import static org.hamcrest.core.IsSame.sameInstance;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChildren;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.mockito.Mockito.stub;
import java.io.File;
@@ -39,16 +42,20 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import javax.jcr.InvalidItemStateException;
import javax.jcr.nodetype.NodeType;
import org.jboss.dna.common.statistic.Stopwatch;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.Node;
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.SessionCache.NodeEditor;
import org.jboss.dna.jcr.cache.Children;
import org.jboss.dna.jcr.cache.NodeInfo;
import org.jboss.dna.jcr.cache.PropertyInfo;
@@ -63,7 +70,6 @@
*/
public class SessionCacheTest {
- private String workspaceName;
private ExecutionContext context;
private JcrNodeTypeManager nodeTypes;
private Stopwatch sw;
@@ -85,8 +91,6 @@
context = new ExecutionContext();
context.getNamespaceRegistry().register("vehix", "http://example.com/vehicles");
- workspaceName = "theWorkspace";
-
stub(session.getExecutionContext()).toReturn(context);
stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
@@ -97,15 +101,9 @@
nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
stub(session.nodeTypeManager()).toReturn(nodeTypes);
- InMemoryRepositorySource source = new InMemoryRepositorySource();
- source.setName("store");
- source.setDefaultWorkspaceName(workspaceName);
- store = Graph.create(source, context);
-
- // Import the "cars.xml" file into the repository
- store.importXmlFrom(new File("src/test/resources/vehicles.xml")).into("/");
-
- cache = new SessionCache(session, workspaceName, context, nodeTypes, store);
+ // Now set up the graph and session cache ...
+ store = getGraph("vehicles"); // imports the "/src/test/resources/vehicles.xml" file
+ cache = getCache("vehicles");
}
/**
@@ -164,8 +162,8 @@
File file ) throws IOException, SAXException {
InMemoryRepositorySource source = new InMemoryRepositorySource();
source.setName(repositoryName);
- source.setDefaultWorkspaceName(workspaceName);
Graph graph = Graph.create(source, context);
+ graph.createWorkspace().named(workspaceName);
if (file != null) {
graph.importXmlFrom(file).into("/");
@@ -229,6 +227,71 @@
}
}
+ protected void assertDoesExist( Graph graph,
+ Location location ) {
+ Node node = store.getNodeAt(location);
+ assertThat(node, is(notNullValue()));
+ assertThat(node.getLocation(), is(location));
+ }
+
+ protected void assertDoesNotExist( Graph graph,
+ Location location ) {
+ try {
+ store.getNodeAt(location);
+ fail("Shouldn't have found the node " + location);
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ }
+
+ protected void assertDoesNotExist( Graph graph,
+ Path path ) {
+ try {
+ store.getNodeAt(path);
+ fail("Shouldn't have found the node " + path);
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ }
+
+ protected void assertDoesNotExist( Graph graph,
+ UUID uuid ) {
+ try {
+ store.getNodeAt(uuid);
+ fail("Shouldn't have found the node " + uuid);
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ }
+
+ protected void assertDoNotExist( Graph graph,
+ List<Location> locations ) {
+ for (Location location : locations)
+ assertDoesNotExist(graph, location);
+ }
+
+ protected void assertDoesNotExist( SessionCache cache,
+ UUID uuid ) throws InvalidItemStateException {
+ assertThat(cache.findNodeInfoInCache(uuid), is(nullValue()));
+ }
+
+ protected void assertIsDeleted( SessionCache cache,
+ UUID uuid ) {
+ try {
+ cache.findNodeInfoInCache(uuid);
+ fail("Shouldn't have found the node " + uuid);
+ } catch (InvalidItemStateException err) {
+ // expected
+ }
+ }
+
+ protected void assertDoesExist( SessionCache cache,
+ UUID uuid ) throws InvalidItemStateException {
+ NodeInfo info = cache.findNodeInfoInCache(uuid);
+ assertThat(info, is(notNullValue()));
+ assertThat(info.getUuid(), is(uuid));
+ }
+
@Test
public void shouldCreateWithValidParameters() {
assertThat(cache, is(notNullValue()));
@@ -386,10 +449,6 @@
@Test
public void shouldFindInfoForNodeUsingRelativePathFromNonRoot() throws Exception {
- String sourceName = "vehicles";
- Graph store = getGraph(sourceName);
- SessionCache cache = getCache(sourceName);
-
// Verify that the node does exist in the source ...
Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
Node carsNode = store.getNodeAt(carsAbsolutePath);
@@ -430,10 +489,6 @@
@Test
public void shouldFindJcrNodeUsingAbsolutePaths() throws Exception {
- String sourceName = "vehicles";
- Graph store = getGraph(sourceName);
- SessionCache cache = getCache(sourceName);
-
// Verify that the node does exist in the source ...
Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
Node carsNode = store.getNodeAt(carsAbsolutePath);
@@ -477,4 +532,99 @@
assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
}
+
+ @Test
+ public void shouldDeleteLeafNode() throws Exception {
+ // Find the state of some Graph nodes we'll be using in the test ...
+ Node utility = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility");
+ Node lr2Node = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR2");
+ Node lr3Node = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ int numChildrenOfUtility = utility.getChildren().size();
+ assertThat(numChildrenOfUtility, is(4));
+ assertThat(utility.getChildren(), hasChildren(segment("vehix:Land Rover LR2"),
+ segment("vehix:Land Rover LR3"),
+ segment("vehix:Hummer H3"),
+ segment("vehix:Ford F-150")));
+
+ // Now get the editor for the 'vehix:Utility' node ...
+ NodeEditor editor = cache.getEditorFor(utility.getLocation().getUuid());
+ assertThat(editor, is(notNullValue()));
+
+ // Destroy the LR3 node, which is a leaf ...
+ editor.destroyChild(lr3Node.getLocation().getUuid());
+
+ // Verify that the store has not yet been changed ...
+ assertDoesExist(store, lr3Node.getLocation());
+
+ // Save the session and verify that the node was deleted ...
+ cache.save();
+ Node utilityNode2 = store.getNodeAt(utility.getLocation());
+ assertThat(utilityNode2.getChildren().size(), is(numChildrenOfUtility - 1));
+ assertThat(utilityNode2.getChildren(), hasChildren(segment("vehix:Land Rover LR2"), // no LR3!
+ segment("vehix:Hummer H3"),
+ segment("vehix:Ford F-150")));
+ // Should no longer find the LR3 node in the graph ...
+ assertDoesNotExist(store, lr3Node.getLocation().getUuid());
+ assertDoesExist(store, lr2Node.getLocation());
+ }
+
+ @Test
+ public void shouldDeleteNonLeafNode() throws Exception {
+ // Find the state of some Graph nodes we'll be using in the test ...
+ Node carsNode = store.getNodeAt("/vehix:Vehicles/vehix:Cars");
+ Node utility = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility");
+ Node hybrid = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Hybrid");
+ Node luxury = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Luxury");
+ Node sports = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Sports");
+ Node lr3 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ Node lr2 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR2");
+ Node f150 = store.getNodeAt("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Ford F-150");
+ int numChildrenOfCars = carsNode.getChildren().size();
+ assertThat(numChildrenOfCars, is(4));
+ assertThat(carsNode.getChildren(), hasChildren(segment("vehix:Hybrid"),
+ segment("vehix:Sports"),
+ segment("vehix:Luxury"),
+ segment("vehix:Utility")));
+
+ // Load the LR2 and Hummer nodes ...
+ NodeInfo h3i = cache.findNodeInfo(utility.getLocation().getUuid(), path("vehix:Hummer H3"));
+ NodeInfo lr2i = cache.findNodeInfo(utility.getLocation().getUuid(), path("vehix:Land Rover LR2"));
+ assertThat(h3i, is(notNullValue()));
+ assertThat(lr2i, is(notNullValue()));
+ assertThat(lr2i.getUuid(), is(lr2.getLocation().getUuid()));
+
+ // Now get the editor for the 'vehix:Cars' node ...
+ NodeEditor editor = cache.getEditorFor(carsNode.getLocation().getUuid());
+ assertThat(editor, is(notNullValue()));
+
+ // Destroy the Utility node, which is NOT a leaf ...
+ editor.destroyChild(utility.getLocation().getUuid());
+
+ // Verify that the store has not yet been changed ...
+ assertDoesExist(store, utility.getLocation());
+
+ // ... but the utility node and its two loaded children have been marked for deletion ...
+ assertIsDeleted(cache, utility.getLocation().getUuid());
+ assertIsDeleted(cache, h3i.getUuid());
+ assertIsDeleted(cache, lr2i.getUuid());
+
+ // Save the session and verify that the Utility node and its children were deleted ...
+ cache.save();
+ Node carsNode2 = store.getNodeAt(carsNode.getLocation());
+ assertThat(carsNode2.getChildren().size(), is(numChildrenOfCars - 1));
+ assertThat(carsNode2.getChildren(),
+ hasChildren(segment("vehix:Hybrid"), segment("vehix:Sports"), segment("vehix:Luxury")));
+ // Should no longer find the Utility node in the graph ...
+ assertDoesNotExist(store, utility.getLocation());
+ assertDoesNotExist(cache, utility.getLocation().getUuid());
+ assertDoesNotExist(cache, h3i.getUuid());
+ assertDoesNotExist(cache, lr2i.getUuid());
+ assertDoesNotExist(cache, lr3.getLocation().getUuid());
+ assertDoesNotExist(cache, f150.getLocation().getUuid());
+ assertDoNotExist(store, utility.getChildren());
+ assertDoesExist(store, hybrid.getLocation());
+ assertDoesExist(store, luxury.getLocation());
+ assertDoesExist(store, sports.getLocation());
+ }
+
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java 2009-03-18 17:05:16 UTC (rev 783)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java 2009-03-20 18:22:45 UTC (rev 784)
@@ -37,13 +37,16 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.Path.Segment;
import org.jboss.dna.jcr.NodeDefinitionId;
import org.junit.Before;
@@ -63,6 +66,7 @@
private NodeDefinitionId definitionId;
private ChangedChildren children;
private Map<Name, PropertyInfo> properties;
+ private Set<Name> mixinTypeNames;
private ChangedNodeInfo changes;
@Before
@@ -78,7 +82,8 @@
definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"));
children = new ChangedChildren(uuid);
properties = new HashMap<Name, PropertyInfo>();
- original = new ImmutableNodeInfo(location, primaryTypeName, definitionId, uuid, children, properties);
+ mixinTypeNames = new HashSet<Name>();
+ original = new ImmutableNodeInfo(location, primaryTypeName, mixinTypeNames, definitionId, uuid, children, properties);
// Create the changed node representation ...
changes = new ChangedNodeInfo(original);
@@ -116,7 +121,7 @@
Name propName = name(name);
PropertyInfo propertyInfo = mock(PropertyInfo.class);
stub(propertyInfo.getPropertyName()).toReturn(propName);
- changes.setProperty(propertyInfo);
+ changes.setProperty(propertyInfo, context.getValueFactories());
return propertyInfo;
}
@@ -137,7 +142,7 @@
// Note that it may not be the same ChildNode instance if another SNS node with smaller index was removed
assertThat(changes.getChildren().getChild(child.getUuid()), is(notNullValue()));
// Now remove the child, making sure the result is the same as the next 'getChildren()' call ...
- assertThat(changes.removeChild(child.getUuid(), pathFactory), is(sameInstance(changes.getChildren())));
+ assertThat(changes.removeChild(child.getUuid(), pathFactory), is(notNullValue()));
// Verify it no longer exists ...
assertThat(changes.getChildren().getChild(child.getUuid()), is(nullValue()));
}
@@ -158,6 +163,28 @@
}
@Test
+ public void shouldHaveMixinTypeNamesFromOriginal() {
+ assertThat(changes.getMixinTypeNames(), is(sameInstance(mixinTypeNames)));
+ }
+
+ @Test
+ public void shouldUpdateMixinTypeNamesWhenSettingJcrMixinTypeProperty() {
+ // Create the DNA property ...
+ Property mixinTypes = context.getPropertyFactory().create(JcrLexicon.MIXIN_TYPES, "dna:type1", "dna:type2");
+
+ // Modify the property ...
+ PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
+ stub(newPropertyInfo.getPropertyName()).toReturn(mixinTypes.getName());
+ stub(newPropertyInfo.getProperty()).toReturn(mixinTypes);
+ PropertyInfo previous = changes.setProperty(newPropertyInfo, context.getValueFactories());
+ assertThat(previous, is(nullValue()));
+
+ // Verify that the mixin types were updated ...
+ assertThat(changes.getProperty(name("jcr:mixinTypes")), is(sameInstance(newPropertyInfo)));
+ assertThat(changes.getMixinTypeNames(), hasItems(name("dna:type2"), name("dna:type1")));
+ }
+
+ @Test
public void shouldHaveNodeDefinitionIdFromOriginal() {
assertThat(changes.getDefinitionId(), is(sameInstance(definitionId)));
}
@@ -428,7 +455,8 @@
// Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
Children before = changes.getChildren();
int beforeSize = before.size();
- Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+ assertThat(changes.removeChild(UUID.randomUUID(), pathFactory), is(nullValue()));
+ Children after = changes.getChildren();
assertThat(after.size(), is(beforeSize));
assertThat(after, is(sameInstance(before)));
assertThat(after, is(sameInstance(changes.getChildren())));
@@ -451,7 +479,8 @@
// Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
Children before = changes.getChildren();
int beforeSize = before.size();
- Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+ assertThat(changes.removeChild(UUID.randomUUID(), pathFactory), is(nullValue()));
+ Children after = changes.getChildren();
assertThat(after.size(), is(beforeSize));
assertThat(after, is(sameInstance(before)));
assertThat(after, is(sameInstance(changes.getChildren())));
@@ -471,7 +500,7 @@
// Modify the property ...
PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
stub(newPropertyInfo.getPropertyName()).toReturn(name("test"));
- PropertyInfo previous = changes.setProperty(newPropertyInfo);
+ PropertyInfo previous = changes.setProperty(newPropertyInfo, context.getValueFactories());
assertThat(previous, is(sameInstance(propertyInfo)));
// Verify we can find the new property ...
15 years, 1 month
DNA SVN: r783 - in trunk/dna-jcr/src: test/java/org/jboss/dna/jcr and 1 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-18 13:05:16 -0400 (Wed, 18 Mar 2009)
New Revision: 783
Added:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
trunk/dna-jcr/src/test/resources/vehicles.xml
Log:
DNA-305 JcrNodeType.canSetProperty Does Not Enforce Constraints
Applied the patch, which is an interim commit to add in the constraint checking. Future changes will remove the JcrSession references from JcrNodeTypeManager, JcrPropertyDefinition, etc. under DNA-302.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Set;
import java.util.Stack;
+import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeDefinition;
@@ -413,6 +414,35 @@
}
/**
+ * Returns <code>true</code> if <code>value</code> can be cast to <code>property.getRequiredType()</code> per the type
+ * conversion rules in section 6.2.6 of the JCR 1.0 specification AND <code>value</code> satisfies the constraints (if any)
+ * for the property definition. If the property definition has a required type of {@link PropertyType#UNDEFINED}, the cast
+ * will be considered to have succeeded and the value constraints (if any) will be interpreted using the semantics for the
+ * type specified in <code>value.getType()</code>.
+ *
+ * @param propertyDefinition the property definition to validate against
+ * @param value the value to be validated
+ * @return <code>true</code> if the value can be cast to the required type for the property definition (if it exists) and
+ * satisfies the constraints for the property (if any exist).
+ * @see PropertyDefinition#getValueConstraints()
+ * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+ */
+ private boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
+ Value value ) {
+ try {
+ assert value instanceof JcrValue : "Illegal implementation of Value interface";
+ ((JcrValue)value).asType(propertyDefinition.getRequiredType());
+
+ return propertyDefinition.satisfiesConstraints(value);
+
+ } catch (javax.jcr.ValueFormatException vfe) {
+ // Cast failed
+ return false;
+ }
+
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value)
@@ -440,14 +470,7 @@
return !property.isMandatory();
}
- try {
- assert value instanceof JcrValue : "Illegal implementation of Value interface";
- ((JcrValue)value).asType(property.getRequiredType());
- } catch (javax.jcr.ValueFormatException vfe) {
- // Cast failed
- return false;
- }
- return true;
+ return canCastToTypeAndMatchesConstraints(property, value);
}
/**
@@ -480,11 +503,7 @@
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
- try {
- assert values[i] instanceof JcrValue : "Illegal implementation of Value interface";
- ((JcrValue)values[i]).asType(property.getRequiredType());
- } catch (javax.jcr.ValueFormatException vfe) {
- // Cast failed
+ if (!canCastToTypeAndMatchesConstraints(property, values[i])) {
return false;
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -23,11 +23,24 @@
*/
package org.jboss.dna.jcr;
+import java.util.regex.Pattern;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
-
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.graph.property.basic.JodaDateTime;
/**
* DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
* through its constructor.
@@ -40,6 +53,7 @@
private final String[] valueConstraints;
private final boolean multiple;
private PropertyDefinitionId id;
+ private ConstraintChecker checker = null;
JcrPropertyDefinition( JcrSession session,
JcrNodeType declaringNodeType,
@@ -120,4 +134,432 @@
this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
}
+
+ boolean satisfiesConstraints( Value value ) {
+ assert value instanceof JcrValue;
+
+ if (valueConstraints == null || valueConstraints.length == 0) {
+ return true;
+ }
+
+ // Neither the 1.0 or 2.0 specification formally prohibit constraints on properties with no required type.
+ int type = requiredType == PropertyType.UNDEFINED ? value.getType() : requiredType;
+
+ /*
+ * Keep a method-local reference to the constraint checker in case another thread attempts to concurrently
+ * check the constraints with a different required type.
+ */
+ ConstraintChecker checker = this.checker;
+
+ if (checker == null || checker.getType() != type) {
+ checker = createChecker(session, type, valueConstraints);
+ this.checker = checker;
+ }
+
+ try {
+ return checker.matches(value);
+ } catch (ValueFormatException vfe) {
+ // The value was so wonky that we couldn't even convert it to an appropriate type
+ return false;
+ }
+ }
+
+ /**
+ * Returns a {@link ConstraintChecker} that will interpret the constraints described by <code>valueConstraints</code> using
+ * the semantics defined in section 6.7.16 of the JCR 1.0 specification for the type indicated by <code>type</code> (where
+ * <code>type</code> is a value from {@link PropertyType}) for the given <code>session</code>. The session is required to
+ * handle the UUID lookups for reference constraints and provides the {@link ExecutionContext} that is used to provide
+ * namespace mappings and value factories for the other constraint checkers.
+ *
+ * @param session the current session
+ * @param type the type of constraint checker that should be created (based on values from {@link PropertyType}).
+ * Type-specific semantics are defined in section 6.7.16 of the JCR 1.0 specification.
+ * @param valueConstraints the constraints for the node as provided by {@link PropertyDefinition#getValueConstraints()}.
+ * @return a constraint checker that matches the given parameters
+ */
+ private ConstraintChecker createChecker( JcrSession session,
+ int type,
+ String[] valueConstraints ) {
+ ExecutionContext context = session.getExecutionContext();
+
+ switch (type) {
+ case PropertyType.BINARY:
+ return new BinaryConstraintChecker(valueConstraints, context);
+ case PropertyType.DATE:
+ return new DateTimeConstraintChecker(valueConstraints, context);
+ case PropertyType.DOUBLE:
+ return new DoubleConstraintChecker(valueConstraints, context);
+ case PropertyType.LONG:
+ return new LongConstraintChecker(valueConstraints, context);
+ case PropertyType.NAME:
+ return new NameConstraintChecker(valueConstraints, context);
+ case PropertyType.PATH:
+ return new PathConstraintChecker(valueConstraints, context);
+ case PropertyType.REFERENCE:
+ return new ReferenceConstraintChecker(valueConstraints, session);
+ case PropertyType.STRING:
+ return new StringConstraintChecker(valueConstraints, context);
+ default:
+ throw new IllegalStateException("Invalid property type: " + type);
+ }
+ }
+
+ /**
+ * Interface that encapsulates a reusable method that can test values to determine if they match a specific list of
+ * constraints for the semantics associated with a single {@link PropertyType}.
+ */
+ public interface ConstraintChecker {
+
+ /**
+ * Returns the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that defines the semantics used for interpretation
+ * for the constraint values.
+ *
+ * @return the {@link PropertyType} (e.g., {@link PropertyType#LONG}) that defines the semantics used for interpretation
+ * for the constraint values
+ */
+ public abstract int getType();
+
+ /**
+ * Returns <code>true</code> if and only if <code>value</code> satisfies the constraints used to create this constraint
+ * checker.
+ *
+ * @param value the value to test
+ * @return whether or not the value satisfies the constraints used to create this constraint checker
+ * @see PropertyDefinition#getValueConstraints()
+ * @see JcrPropertyDefinition#satisfiesConstraints(Value)
+ */
+ public abstract boolean matches( Value value );
+ }
+
+ private interface Range<T> {
+ boolean accepts( T value );
+ }
+
+ /**
+ * Encapsulation of common parsing logic used for all ranged constraints. Binary, long, double, and date values all have their
+ * constraints interpreted as a set of ranges that may include or exclude each end-point in the range.
+ *
+ * @param <T> the specific type of the constraint (e.g., Binary, Long, Double, or DateTime).
+ */
+ private static abstract class RangeConstraintChecker<T extends Comparable<T>> implements ConstraintChecker {
+ private final Range<T>[] constraints;
+ private final ValueFactory<T> valueFactory;
+
+ @SuppressWarnings( "unchecked" )
+ protected RangeConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ constraints = new Range[valueConstraints.length];
+ this.valueFactory = getValueFactory(context.getValueFactories());
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = parseValueConstraint(valueConstraints[i]);
+ }
+ }
+
+ protected abstract ValueFactory<T> getValueFactory( ValueFactories valueFactories );
+
+ protected abstract Comparable<T> parseValue( String s );
+
+ /**
+ * Parses one constraint value into a {@link Range} that will accept only values which match the range described by the
+ * value constraint.
+ *
+ * @param valueConstraint the individual value constraint to be parsed into a {@link Range}.
+ * @return a range that accepts values which match the given value constraint.
+ */
+ private Range<T> parseValueConstraint( String valueConstraint ) {
+ assert valueConstraint != null;
+
+ final boolean includeLower = valueConstraint.charAt(0) == '[';
+ final boolean includeUpper = valueConstraint.charAt(valueConstraint.length() - 1) == ']';
+
+ int commaInd = valueConstraint.indexOf(',');
+ String lval = commaInd > 1 ? valueConstraint.substring(1, commaInd) : null;
+ String rval = commaInd < valueConstraint.length() - 2 ? valueConstraint.substring(commaInd + 1,
+ valueConstraint.length() - 1) : null;
+
+ final Comparable<T> lower = lval == null ? null : parseValue(lval.trim());
+ final Comparable<T> upper = rval == null ? null : parseValue(rval.trim());
+
+ return new Range<T>() {
+ public boolean accepts( T value ) {
+ if (lower != null && (includeLower ? lower.compareTo(value) > 0 : lower.compareTo(value) >= 0)) {
+ return false;
+ }
+ if (upper != null && (includeUpper ? upper.compareTo(value) < 0 : upper.compareTo(value) <= 0)) {
+ return false;
+ }
+ return true;
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see ConstraintChecker#matches(javax.jcr.Value)
+ */
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ T convertedValue = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].accepts(convertedValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class BinaryConstraintChecker extends LongConstraintChecker {
+ private final ValueFactories valueFactories;
+
+ protected BinaryConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+
+ this.valueFactories = context.getValueFactories();
+ }
+
+ @Override
+ public int getType() {
+ return PropertyType.BINARY;
+ }
+
+ @Override
+ public boolean matches( Value value ) {
+ Binary binary = valueFactories.getBinaryFactory().create(((JcrValue)value).value());
+ return super.matches(new JcrValue(valueFactories, PropertyType.LONG, binary.getSize()));
+ }
+ }
+
+ @Immutable
+ private static class LongConstraintChecker extends RangeConstraintChecker<Long> {
+
+ protected LongConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.LONG;
+ }
+
+ @Override
+ protected ValueFactory<Long> getValueFactory( ValueFactories valueFactories ) {
+ return valueFactories.getLongFactory();
+ }
+
+ @Override
+ protected Comparable<Long> parseValue( String s ) {
+ return Long.parseLong(s);
+ }
+ }
+
+ @Immutable
+ private static class DateTimeConstraintChecker extends RangeConstraintChecker<DateTime> {
+
+ protected DateTimeConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.DATE;
+ }
+
+ @Override
+ protected ValueFactory<DateTime> getValueFactory( ValueFactories valueFactories ) {
+ return valueFactories.getDateFactory();
+ }
+
+ @Override
+ protected Comparable<DateTime> parseValue( String s ) {
+ return new JodaDateTime(s.trim());
+ }
+ }
+
+ @Immutable
+ private static class DoubleConstraintChecker extends RangeConstraintChecker<Double> {
+
+ protected DoubleConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ super(valueConstraints, context);
+ }
+
+ public int getType() {
+ return PropertyType.DOUBLE;
+ }
+
+ @Override
+ protected ValueFactory<Double> getValueFactory( ValueFactories valueFactories ) {
+ return valueFactories.getDoubleFactory();
+ }
+
+ @Override
+ protected Comparable<Double> parseValue( String s ) {
+ return Double.parseDouble(s);
+ }
+ }
+
+ @Immutable
+ private static class ReferenceConstraintChecker implements ConstraintChecker {
+ private final JcrSession session;
+
+ private final Name[] constraints;
+
+ protected ReferenceConstraintChecker( String[] valueConstraints,
+ JcrSession session ) {
+ this.session = session;
+
+ NameFactory factory = session.getExecutionContext().getValueFactories().getNameFactory();
+
+ constraints = new Name[valueConstraints.length];
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = factory.create(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.REFERENCE;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ Node node = null;
+ try {
+ node = session.getNodeByUUID(((JcrValue)value).value().toString());
+ } catch (RepositoryException re) {
+ return false;
+ }
+
+ for (int i = 0; i < constraints.length; i++) {
+ try {
+ if (node.isNodeType(constraints[i].getString(session.getExecutionContext().getNamespaceRegistry()))) {
+ return true;
+ }
+ } catch (RepositoryException re) {
+ throw new IllegalStateException(re);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class NameConstraintChecker implements ConstraintChecker {
+ private final Name[] constraints;
+ private final ValueFactory<Name> valueFactory;
+
+ protected NameConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ this.valueFactory = context.getValueFactories().getNameFactory();
+
+ constraints = new Name[valueConstraints.length];
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = valueFactory.create(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.NAME;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ Name name = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class StringConstraintChecker implements ConstraintChecker {
+ private final Pattern[] constraints;
+ private ValueFactory<String> valueFactory;
+
+ protected StringConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ constraints = new Pattern[valueConstraints.length];
+ this.valueFactory = context.getValueFactories().getStringFactory();
+
+ for (int i = 0; i < valueConstraints.length; i++) {
+ constraints[i] = Pattern.compile(valueConstraints[i]);
+ }
+ }
+
+ public int getType() {
+ return PropertyType.STRING;
+ }
+
+ public boolean matches( Value value ) {
+ assert value != null;
+
+ String convertedValue = valueFactory.create(((JcrValue)value).value());
+
+ for (int i = 0; i < constraints.length; i++) {
+ if (constraints[i].matcher(convertedValue).matches()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ @Immutable
+ private static class PathConstraintChecker implements ConstraintChecker {
+ private final ExecutionContext context;
+ private final String[] constraints;
+
+ protected PathConstraintChecker( String[] valueConstraints,
+ ExecutionContext context ) {
+ this.constraints = valueConstraints;
+ this.context = context;
+ }
+
+ public int getType() {
+ return PropertyType.PATH;
+ }
+
+ public boolean matches( Value valueToMatch ) {
+ assert valueToMatch != null;
+
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path value = pathFactory.create(((JcrValue)valueToMatch).value());
+ value = value.getNormalizedPath();
+
+ for (int i = 0; i < constraints.length; i++) {
+ boolean matchesDescendants = constraints[i].endsWith("*");
+ Path constraintPath = pathFactory.create(matchesDescendants ? constraints[i].substring(0,
+ constraints[i].length() - 2) : constraints[i]);
+
+ if (matchesDescendants && value.isDecendantOf(constraintPath)) {
+ return true;
+ }
+
+ if (!matchesDescendants && value.equals(constraintPath)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrValue.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -71,6 +71,16 @@
}
/**
+ * Returns a direct reference to the internal value object wrapped by this {@link JcrValue}. Useful to avoid the expense of
+ * {@link #asType(int)} if the caller already knows the type of the value.
+ *
+ * @return a reference to the {@link #value} field.
+ */
+ final Object value() {
+ return value;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.Value#getBoolean()
@@ -240,8 +250,8 @@
Segment[] segments = path.getSegmentsArray();
if (!path.isAbsolute() && segments.length == 1 && !segments[0].hasIndex()) {
- return new JcrValue(this.valueFactories, type, this.valueFactories.getNameFactory()
- .create(valueAsString));
+ return new JcrValue(this.valueFactories, type,
+ this.valueFactories.getNameFactory().create(valueAsString));
}
}
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -0,0 +1,629 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * Indirectly tests the JcrConstaintCheckerFactory through {@link JcrPropertyDefinition#satisfiesConstraints(Value)},
+ * which provides the wrapper around the factory that the rest of the API is expected to utilize.
+ *
+ */
+public class JcrPropertyDefinitionTest {
+
+ private final String[] EXPECTED_BINARY_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
+ private final String[] EXPECTED_DATE_CONSTRAINTS = new String[] { "[,+1945-08-01T01:30:00.000Z]", "[+1975-08-01T01:30:00.000Z,)"};
+ private final String[] EXPECTED_DOUBLE_CONSTRAINTS = new String[] { "[,5.0)", "[10.1, 20.2)", "(30.3,40.4]", "[50.5,]" };
+ private final String[] EXPECTED_LONG_CONSTRAINTS = new String[] { "[,5)", "[10, 20)", "(30,40]", "[50,]" };
+ private final String[] EXPECTED_NAME_CONSTRAINTS = new String[] { "jcr:system", "dnatest:constrainedType" };
+ //private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c" };
+ private final String[] EXPECTED_PATH_CONSTRAINTS = new String[] {"/jcr:system/*", "b", "/a/b/c" };
+ private final String[] EXPECTED_REFERENCE_CONSTRAINTS = new String[] { "dna:root" };
+ private final String[] EXPECTED_STRING_CONSTRAINTS = new String[] { "foo", "bar*", ".*baz" };
+
+ private String workspaceName;
+ private ExecutionContext context;
+ private InMemoryRepositorySource source;
+ private JcrWorkspace workspace;
+ private JcrSession session;
+ private Graph graph;
+ private RepositoryConnectionFactory connectionFactory;
+ private NodeTypeManager nodeTypeManager;
+ private Map<String, Object> sessionAttributes;
+ @Mock
+ private JcrRepository repository;
+
+ @Before
+ public void beforeEach() throws Exception {
+ workspaceName = "workspace1";
+ final String repositorySourceName = "repository";
+
+ // Set up the source ...
+ source = new InMemoryRepositorySource();
+ source.setName(workspaceName);
+ source.setDefaultWorkspaceName(workspaceName);
+
+ // Set up the execution context ...
+ context = new ExecutionContext();
+ // Register the test namespace
+ context.getNamespaceRegistry().register(TestLexicon.Namespace.PREFIX, TestLexicon.Namespace.URI);
+
+ // Set up the initial content ...
+ graph = Graph.create(source, context);
+
+ // Make sure the path to the namespaces exists ...
+ graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
+ graph.create("/a").and().create("/a/b").and().create("/a/b/c");
+
+ graph.set("jcr:mixinTypes").on("/a").to(JcrMixLexicon.REFERENCEABLE);
+
+ // Stub out the connection factory ...
+ connectionFactory = new RepositoryConnectionFactory() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
+ */
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+ return repositorySourceName.equals(sourceName) ? source.getConnection() : null;
+ }
+ };
+
+ // Stub out the repository, since we only need a few methods ...
+ MockitoAnnotations.initMocks(this);
+ stub(repository.getRepositorySourceName()).toReturn(repositorySourceName);
+ stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+
+ // Set up the session attributes ...
+ sessionAttributes = new HashMap<String, Object>();
+ sessionAttributes.put("attribute1", "value1");
+
+ // Now create the workspace ...
+ workspace = new JcrWorkspace(repository, workspaceName, context, sessionAttributes);
+
+ // Create the session and log in ...
+ session = (JcrSession)workspace.getSession();
+
+ nodeTypeManager = new JcrNodeTypeManager(this.session,
+ new TestNodeTypeSource(this.session,
+ new DnaBuiltinNodeTypeSource(this.session,
+ new JcrBuiltinNodeTypeSource(this.session))));
+ stub(workspace.getNodeTypeManager()).toReturn(nodeTypeManager);
+
+ }
+
+ @After
+ public void after() throws Exception {
+ if (session.isLive()) {
+ session.logout();
+ }
+ }
+
+ private JcrPropertyDefinition propertyDefinitionFor(NodeType nodeType, Name propertyName) {
+ PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+ String property = propertyName.getString(context.getNamespaceRegistry());
+
+ for (int i = 0; i < propertyDefs.length; i++) {
+ if (propertyDefs[i].getName().equals(property)) {
+ return (JcrPropertyDefinition) propertyDefs[i];
+ }
+ }
+ throw new IllegalStateException("Could not find property definition name " + property + " for type "
+ + nodeType.getName() + ". Test setup is invalid.");
+ }
+
+ private void checkConstraints(NodeType nodeType, Name propertyName, String[] expectedConstraints) {
+ PropertyDefinition propertyDefs[] = nodeType.getPropertyDefinitions();
+ String property = propertyName.getString(context.getNamespaceRegistry());
+ String[] constraints = null;
+
+ for (int i = 0; i < propertyDefs.length; i++) {
+ if (propertyDefs[i].getName().equals(property)) {
+ constraints = propertyDefs[i].getValueConstraints();
+ break;
+ }
+ }
+
+ if (!Arrays.equals(constraints, expectedConstraints)) {
+ throw new IllegalStateException("Unexpected constraints for property: " + property);
+ }
+ }
+
+ private NodeType validateTypeDefinition() throws Exception {
+ NamespaceRegistry nsr = context.getNamespaceRegistry();
+
+ NodeType constrainedType = nodeTypeManager.getNodeType(TestLexicon.CONSTRAINED_TYPE.getString(nsr));
+ assertThat(constrainedType, notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE), notNullValue());
+ assertThat(propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING), notNullValue());
+
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_BINARY, EXPECTED_BINARY_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DATE, EXPECTED_DATE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_DOUBLE, EXPECTED_DOUBLE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_LONG, EXPECTED_LONG_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_NAME, EXPECTED_NAME_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_PATH, EXPECTED_PATH_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_REFERENCE, EXPECTED_REFERENCE_CONSTRAINTS);
+ checkConstraints(constrainedType, TestLexicon.CONSTRAINED_STRING, EXPECTED_STRING_CONSTRAINTS);
+
+ return constrainedType;
+ }
+
+ private Value valueFor(Object value, int jcrType) {
+ return new JcrValue(context.getValueFactories(), jcrType, value);
+ }
+
+ private String stringOfLength(int length) {
+ StringBuffer buff = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ buff.append(i % 10);
+ }
+
+ return buff.toString();
+ }
+
+ private boolean satisfiesConstraints(JcrPropertyDefinition property, Value[] values) {
+ for (int i = 0; i < values.length; i++) {
+ if (!property.satisfiesConstraints(values[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Test( expected = AssertionError.class )
+ public void shouldNotAllowNullValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+ assertThat(prop.satisfiesConstraints((Value)null), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidBinaryValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(0), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(4), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(10), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(19), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(31), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(40), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(50), PropertyType.BINARY)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(99), PropertyType.BINARY)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidBinaryValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+ Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+ valueFor(stringOfLength(10), PropertyType.BINARY), valueFor(stringOfLength(19), PropertyType.BINARY),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(0), PropertyType.BINARY)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidBinaryValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(5), PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(9), PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(20), PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(30), PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(41), PropertyType.BINARY)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(stringOfLength(49), PropertyType.BINARY)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidBinaryValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_BINARY);
+
+ // Making assumption that String.getBytes().length = String.length() on the platform's default encoding
+ Value[] values = new Value[] {valueFor(stringOfLength(4), PropertyType.BINARY),
+ valueFor(stringOfLength(10), PropertyType.BINARY), valueFor(stringOfLength(20), PropertyType.BINARY),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(stringOfLength(9), PropertyType.BINARY)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidDateValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+ assertThat(prop.satisfiesConstraints(valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("+1945-07-31T01:30:00.000", PropertyType.DATE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("+0001-08-01T01:30:00.000", PropertyType.DATE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:30:00.000", PropertyType.DATE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:31:00.000", PropertyType.DATE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("+2009-08-01T01:30:00.000", PropertyType.DATE)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidDateValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+ Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE),
+ valueFor("+2009-08-01T01:30:00.000", PropertyType.DATE),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("+1975-08-01T01:31:00.000", PropertyType.DATE)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDateValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+ assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.001Z", PropertyType.DATE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("+1975-08-01T01:29:59.999Z", PropertyType.DATE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("+1945-08-01T01:30:00.000-05:00", PropertyType.DATE)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDateValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DATE);
+
+ Value[] values = new Value[] {valueFor("-1945-08-01T01:30:00.000", PropertyType.DATE),
+ valueFor("+1945-08-01T01:30:00.000-05:00", PropertyType.DATE),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("+1945-08-01T01:30:00.001Z", PropertyType.DATE)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidDoubleValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+ assertThat(prop.satisfiesConstraints(valueFor(Double.MIN_VALUE, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(20.19, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(30.31, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(40.4, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(50.5, PropertyType.DOUBLE)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(Double.MAX_VALUE, PropertyType.DOUBLE)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidDoubleValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19, PropertyType.DOUBLE),
+ valueFor(50.5, PropertyType.DOUBLE)};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99, PropertyType.DOUBLE)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDoubleValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+ assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.DOUBLE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(9.99999999, PropertyType.DOUBLE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(20.2, PropertyType.DOUBLE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(30.3, PropertyType.DOUBLE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(40.41, PropertyType.DOUBLE)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(49.9, PropertyType.DOUBLE)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidDoubleValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_DOUBLE);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.DOUBLE), valueFor(20.19, PropertyType.DOUBLE),
+ valueFor(50.49, PropertyType.DOUBLE)};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(20.2, PropertyType.DOUBLE)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidLongValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+ assertThat(prop.satisfiesConstraints(valueFor(Long.MIN_VALUE, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(0.1, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(4.99, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(10.100, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(19, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(31, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(40, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(50, PropertyType.LONG)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor(Long.MAX_VALUE, PropertyType.LONG)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidLongValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10, PropertyType.LONG),
+ valueFor(50, PropertyType.LONG)};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(4.99, PropertyType.LONG)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidLongValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+ assertThat(prop.satisfiesConstraints(valueFor(5, PropertyType.LONG)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(9, PropertyType.LONG)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(20, PropertyType.LONG)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(30, PropertyType.LONG)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(41, PropertyType.LONG)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor(49, PropertyType.LONG)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidLongValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_LONG);
+
+ Value[] values = new Value[] {valueFor(0.1, PropertyType.LONG), valueFor(10, PropertyType.LONG),
+ valueFor(49, PropertyType.LONG)};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor(30, PropertyType.LONG)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidNameValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+ assertThat(prop.satisfiesConstraints(valueFor("jcr:system", PropertyType.NAME)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType", PropertyType.NAME)), is(true));
+
+ // Test that names work across namespace remaps
+ session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+ assertThat(prop.satisfiesConstraints(valueFor("newprefix:constrainedType", PropertyType.NAME)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidNameValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+ Value[] values = new Value[] {valueFor("jcr:system", PropertyType.NAME),
+ valueFor("dnatest:constrainedType", PropertyType.NAME),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("jcr:system", PropertyType.NAME)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidNameValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+ assertThat(prop.satisfiesConstraints(valueFor("system", PropertyType.NAME)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("jcr:system2", PropertyType.NAME)), is(false));
+
+ // Test that old names fail after namespace remaps
+ session.setNamespacePrefix("newprefix", TestLexicon.Namespace.URI);
+ assertThat(prop.satisfiesConstraints(valueFor("dnatest:constrainedType", PropertyType.NAME)), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidNameValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_NAME);
+
+ Value[] values = new Value[] {valueFor("jcr:system", PropertyType.NAME),
+ valueFor("dnatest:constrainedType2", PropertyType.NAME),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("jcr:system2", PropertyType.NAME)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidStringValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+ assertThat(prop.satisfiesConstraints(valueFor("foo", PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("bar", PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("barr", PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("barrrrrrrrr", PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("baz", PropertyType.STRING)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("shabaz", PropertyType.STRING)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidStringValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+ Value[] values = new Value[] {valueFor("foo", PropertyType.STRING), valueFor("barr", PropertyType.STRING),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("baz", PropertyType.STRING)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidStringValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+ assertThat(prop.satisfiesConstraints(valueFor("", PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("foot", PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("abar", PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("bard", PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("baz!", PropertyType.STRING)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("bazzat", PropertyType.STRING)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidStringValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_STRING);
+
+ Value[] values = new Value[] {valueFor("foo", PropertyType.STRING), valueFor("bard", PropertyType.STRING),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("bazzat", PropertyType.STRING)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidPathValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+ assertThat(prop.satisfiesConstraints(valueFor("b", PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c", PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:namespace", PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/", PropertyType.PATH)), is(true));
+ assertThat(prop.satisfiesConstraints(valueFor("/jcr:system/dna:foo", PropertyType.PATH)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidPathValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+ Value[] values = new Value[] {valueFor("b", PropertyType.PATH), valueFor("/a/b/c", PropertyType.PATH),};
+ assertThat(satisfiesConstraints(prop, new Value[] {}), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a/b/c", PropertyType.PATH)}), is(true));
+ assertThat(satisfiesConstraints(prop, values), is(true));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidPathValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+ assertThat(prop.satisfiesConstraints(valueFor("a", PropertyType.PATH)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b", PropertyType.PATH)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/jcr:system", PropertyType.PATH)), is(false));
+ assertThat(prop.satisfiesConstraints(valueFor("/a/b/c/d", PropertyType.PATH)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidPathValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_PATH);
+
+ Value[] values = new Value[] {valueFor("b", PropertyType.PATH), valueFor("/a/b/c/d", PropertyType.PATH),};
+ assertThat(satisfiesConstraints(prop, new Value[] {valueFor("/a", PropertyType.PATH)}), is(false));
+ assertThat(satisfiesConstraints(prop, values), is(false));
+ }
+
+ @Test
+ public void shouldAllowValidReferenceValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+
+ UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+ assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(true));
+ }
+
+ @Test
+ public void shouldAllowValidReferenceValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+
+ UUID uuid = ((AbstractJcrNode) session.getRootNode()).nodeUuid;
+
+ assertThat(satisfiesConstraints(prop, new Value[] { }), is(true));
+ assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(true));
+ }
+
+
+ @Test
+ public void shouldNotAllowInvalidReferenceValue() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+
+ Node aNode = session.getRootNode().getNode("a");
+ UUID uuid = ((JcrNode) aNode).nodeUuid;
+
+
+ assertThat(prop.satisfiesConstraints(valueFor(uuid, PropertyType.REFERENCE)), is(false));
+ }
+
+ @Test
+ public void shouldNotAllowInvalidReferenceValues() throws Exception {
+ NodeType constrainedType = validateTypeDefinition();
+ JcrPropertyDefinition prop = propertyDefinitionFor(constrainedType, TestLexicon.CONSTRAINED_REFERENCE);
+
+ Node aNode = session.getRootNode().getNode("a");
+ UUID uuid = ((JcrNode) aNode).nodeUuid;
+
+ assertThat(satisfiesConstraints(prop, new Value[] { valueFor(uuid, PropertyType.REFERENCE) }), is(false));
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyDefinitionTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -0,0 +1,50 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * Namespace and names for DNA testing.
+ *
+ */
+public class TestLexicon {
+
+ public static class Namespace {
+ public static final String URI = "http://www.jboss.org/dna/test/1.0";
+ public static final String PREFIX = "dnatest";
+ }
+
+ public static final Name CONSTRAINED_TYPE = new BasicName(Namespace.URI, "constrainedType");
+ public static final Name CONSTRAINED_BINARY = new BasicName(Namespace.URI, "constrainedBinary");
+ public static final Name CONSTRAINED_DATE = new BasicName(Namespace.URI, "constrainedDate");
+ public static final Name CONSTRAINED_DOUBLE = new BasicName(Namespace.URI, "constrainedDouble");
+ public static final Name CONSTRAINED_LONG = new BasicName(Namespace.URI, "constrainedLong");
+ public static final Name CONSTRAINED_NAME = new BasicName(Namespace.URI, "constrainedName");
+ public static final Name CONSTRAINED_PATH = new BasicName(Namespace.URI, "constrainedPath");
+ public static final Name CONSTRAINED_REFERENCE = new BasicName(Namespace.URI, "constrainedReference");
+ public static final Name CONSTRAINED_STRING = new BasicName(Namespace.URI, "constrainedString");
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestLexicon.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java 2009-03-18 17:05:16 UTC (rev 783)
@@ -0,0 +1,145 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.jcr.PropertyType;
+import javax.jcr.nodetype.NodeType;
+
+/**
+ * Node type source with additional node types that can be used for testing. This class defines its own namespace for its types.
+ */
+public class TestNodeTypeSource extends AbstractJcrNodeTypeSource {
+
+ /** The list of primary node types. */
+ private final List<JcrNodeType> primaryNodeTypes;
+ /** The list of mixin node types. */
+ private final List<JcrNodeType> mixinNodeTypes;
+
+ TestNodeTypeSource( JcrSession session,
+ JcrNodeTypeSource predecessor ) {
+ super(predecessor);
+
+ primaryNodeTypes = new ArrayList<JcrNodeType>();
+ mixinNodeTypes = new ArrayList<JcrNodeType>();
+
+ JcrNodeType base = findType(JcrNtLexicon.BASE);
+
+ if (base == null) {
+ String baseTypeName = JcrNtLexicon.BASE.getString(session.getExecutionContext().getNamespaceRegistry());
+ String namespaceTypeName = DnaLexicon.NAMESPACE.getString(session.getExecutionContext().getNamespaceRegistry());
+ throw new IllegalStateException(JcrI18n.supertypeNotFound.text(baseTypeName, namespaceTypeName));
+ }
+
+ // Stubbing in child node and property definitions for now
+ JcrNodeType constrainedType = new JcrNodeType(
+ session,
+ TestLexicon.CONSTRAINED_TYPE,
+ Arrays.asList(new NodeType[] {base}),
+ NO_PRIMARY_ITEM_NAME,
+ NO_CHILD_NODES,
+ Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(session, null,
+ TestLexicon.CONSTRAINED_BINARY,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.BINARY, new String[] {"[,5)",
+ "[10, 20)", "(30,40]", "[50,]"}, false),
+ new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_DATE,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.DATE, new String[] {
+ "[,+1945-08-01T01:30:00.000Z]",
+ "[+1975-08-01T01:30:00.000Z,)"}, false),
+ new JcrPropertyDefinition(session, null,
+ TestLexicon.CONSTRAINED_DOUBLE,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.DOUBLE,
+ new String[] {"[,5.0)", "[10.1, 20.2)",
+ "(30.3,40.4]", "[50.5,]"}, false),
+
+ new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_LONG,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.LONG, new String[] {"[,5)",
+ "[10, 20)", "(30,40]", "[50,]"}, false),
+
+ new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_NAME,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.NAME, new String[] {
+ "jcr:system", "dnatest:constrainedType"},
+ false),
+
+ new JcrPropertyDefinition(session, null, TestLexicon.CONSTRAINED_PATH,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.PATH, new String[] {
+ // "/" + JcrLexicon.Namespace.URI + ":system/*", "b", "/a/b/c"}, false),
+ "/jcr:system/*", "b", "/a/b/c"}, false),
+ new JcrPropertyDefinition(session, null,
+ TestLexicon.CONSTRAINED_REFERENCE,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.REFERENCE,
+ new String[] {"dna:root",}, false),
+
+ new JcrPropertyDefinition(session, null,
+ TestLexicon.CONSTRAINED_STRING,
+ OnParentVersionBehavior.IGNORE.getJcrValue(),
+ false, false, false, NO_DEFAULT_VALUES,
+ PropertyType.STRING, new String[] {"foo",
+ "bar*", ".*baz",}, false),
+
+ }), NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+
+ primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {constrainedType}));
+ mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+ return primaryNodeTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+ return mixinNodeTypes;
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/TestNodeTypeSource.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/resources/vehicles.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/vehicles.xml 2009-03-18 16:54:54 UTC (rev 782)
+++ trunk/dna-jcr/src/test/resources/vehicles.xml 2009-03-18 17:05:16 UTC (rev 783)
@@ -27,7 +27,7 @@
<Vehicles xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns="http://example.com/vehicles">
<Cars>
<Hybrid>
- <car jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
+ <car jcr:name="Toyota Prius" jcr:mixinTypes="mix:referenceable" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
<car jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
<car jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
</Hybrid>
15 years, 1 month
DNA SVN: r782 - in trunk/dna-jcr/src: test/java/org/jboss/dna/jcr/cache and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-18 12:54:54 -0400 (Wed, 18 Mar 2009)
New Revision: 782
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
Log:
DNA-194 Implement update JCR capability
Improved the mutable versions of the cached reprentations, and added a large number of unit tests. The mutable objects should be ready to track adding/removing children and adding/changing/removing properties. Making the SessionCache to use them is next.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -68,31 +68,34 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(org.jboss.dna.jcr.cache.ChildNode,
- * org.jboss.dna.graph.property.PathFactory)
+ * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
*/
@Override
- public ChangedChildren without( ChildNode child,
+ public ChangedChildren without( UUID childUuid,
PathFactory pathFactory ) {
- // Make sure this object contains the child ...
- if (!childrenByUuid.containsKey(child.getUuid())) {
+ // Remove the object that has the same UUID (regardless of the current SNS index) ...
+ ChildNode toBeRemoved = childrenByUuid.get(childUuid);
+ if (toBeRemoved == null) {
return this;
}
- // Remove the child fro this object, then adjust the remaining child node instances that follow it ...
- Name childName = child.getName();
+ // Remove the child from this object, then adjust the remaining child node instances that follow it ...
+ Name childName = toBeRemoved.getName();
List<ChildNode> childrenWithSameName = childrenByName.get(childName);
- int snsIndex = child.getSnsIndex();
+ int snsIndex = toBeRemoved.getSnsIndex();
if (snsIndex > childrenWithSameName.size()) {
// The child node (with that SNS index) is no longer here) ...
return this;
}
ListIterator<ChildNode> iter = childrenWithSameName.listIterator(--snsIndex);
assert iter.hasNext();
- iter.next(); // start ...
+ ChildNode willBeRemoved = iter.next();
+ assert willBeRemoved == toBeRemoved;
+ childrenByUuid.remove(toBeRemoved.getUuid());
iter.remove(); // removes the item that was last returned from 'next()'
while (iter.hasNext()) {
ChildNode next = iter.next();
ChildNode newNext = next.with(pathFactory.createSegment(childName, ++snsIndex));
+ childrenByUuid.put(newNext.getUuid(), newNext);
iter.set(newNext);
}
return this;
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,323 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ * <p>
+ * Each instance maintains a reference to the original (usually immutable) NodeInfo representation that was probably read from the
+ * repository.
+ */
+@Immutable
+public class ChangedNodeInfo implements NodeInfo {
+
+ protected static final PropertyInfo DELETED_PROPERTY = null;
+
+ /**
+ * A reference to the original representation of the node.
+ */
+ private final NodeInfo original;
+
+ /**
+ * The new parent for this node if it has been changed, or null if the parent has not be changed.
+ */
+ private UUID newParent;
+
+ /**
+ * The updated children, or null if the children have not been changed from the original's.
+ */
+ private ChangedChildren changedChildren;
+
+ /**
+ * This map, if it is non-null, contains the changed properties, overriding whatever is in the original. If a property is
+ * removed from the original, an entry is added to this map with the name of the removed property and a null PropertyInfo.
+ */
+ private Map<Name, PropertyInfo> changedProperties;
+
+ /**
+ * Create an immutable NodeInfo instance.
+ *
+ * @param original the original node information, may not be null
+ */
+ public ChangedNodeInfo( NodeInfo original ) {
+ assert original != null;
+ this.original = original;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getOriginalLocation()
+ */
+ public Location getOriginalLocation() {
+ return original.getOriginalLocation();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getUuid()
+ */
+ public UUID getUuid() {
+ return original.getUuid();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getParent()
+ */
+ public UUID getParent() {
+ // Even if this is used for recording changes to the root node (which has no parent),
+ // the root node cannot be moved to a different node (and no other node can be moved to
+ // the root). Therefore, if this represents the root node, the original's parent UUID will
+ // be the correct parent (null), and this representation will not need to have a different
+ // (non-null) value.
+ if (newParent != null) return newParent;
+ return original.getParent();
+ }
+
+ /**
+ * Record that this node has been moved under a new parent. This method does <i>not</i> change the ChildNode references in the
+ * old or new parent.
+ *
+ * @param parent the new parent, or null if the original's parent should be used
+ * @return the previous parent (either the original's or the last new parent); may be null
+ */
+ public UUID setParent( UUID parent ) {
+ UUID result = newParent != null ? newParent : original.getParent(); // may still be null
+ newParent = parent;
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPrimaryTypeName()
+ */
+ public Name getPrimaryTypeName() {
+ return original.getPrimaryTypeName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
+ */
+ public NodeDefinitionId getDefinitionId() {
+ return original.getDefinitionId();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
+ */
+ public Children getChildren() {
+ if (changedChildren != null) return changedChildren;
+ return original.getChildren();
+ }
+
+ /**
+ * Add a child to the children. This method does nothing if the child is already in the children.
+ *
+ * @param childName the name of the child that is to be added; may not be null
+ * @param childUuid the UUID of the child that is to be added; may not be null
+ * @param factory the path factory that should be used to create a {@link Segment} for the new {@link ChildNode} object
+ * @return the child node that was just added; never null
+ */
+ public ChildNode addChild( Name childName,
+ UUID childUuid,
+ PathFactory factory ) {
+ if (changedChildren == null) {
+ // We need to capture the original children as a changed contained ...
+ changedChildren = new ChangedChildren(original.getChildren());
+ }
+ return changedChildren.add(childName, childUuid, factory);
+ }
+
+ /**
+ * Remove a child from the children. This method only uses the child's UUID to identify the contained ChildNode instance that
+ * should be removed.
+ * <p>
+ * Note that this method returns the new {@link Children} container, which is the same as would be returned by
+ * {@link #getChildren()} called immediately after this method.
+ * </p>
+ *
+ * @param childUUID the UUID of the child that is to be removed; may not be null
+ * @param factory the path factory that should be used to create a {@link Segment} for replacement {@link ChildNode} objects
+ * for nodes with the same name that and higher same-name-sibiling indexes.
+ * @return the Children object that has the modified children
+ */
+ public Children removeChild( UUID childUUID,
+ PathFactory factory ) {
+ if (changedChildren == null) {
+ // Create the changed children. First check whether there are 0 or 1 child ...
+ Children existing = original.getChildren();
+ int numExisting = existing.size();
+ if (numExisting == 0) {
+ // nothing to do, so return the original's children
+ return existing;
+ }
+ if (existing.getChild(childUUID) == null) {
+ // The requested child doesn't exist in the children, so return silently ...
+ return existing;
+ }
+ if (numExisting == 1) {
+ // We're removing the only child in the original ...
+ changedChildren = new ChangedChildren(existing.getParentUuid());
+ return changedChildren;
+ }
+ // There is at least one child, so create the new children container ...
+ assert existing instanceof InternalChildren;
+ InternalChildren internal = (InternalChildren)existing;
+ changedChildren = internal.without(childUUID, factory);
+ } else {
+ changedChildren = changedChildren.without(childUUID, factory);
+ }
+ return changedChildren;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#hasProperties()
+ */
+ public boolean hasProperties() {
+ if (changedProperties == null) return original.hasProperties();
+ int numUnchanged = original.getPropertyCount();
+ int numChangedOrDeleted = changedProperties.size();
+ if (numUnchanged > numChangedOrDeleted) return true; // more unchanged than could be deleted
+ // They could all be changed or deleted, so we need to find one changed property ...
+ for (Map.Entry<Name, PropertyInfo> entry : changedProperties.entrySet()) {
+ if (entry.getValue() != DELETED_PROPERTY) return true;
+ }
+ return false; // all properties must have been deleted ...
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyCount()
+ */
+ public int getPropertyCount() {
+ int numUnchanged = original.getPropertyCount();
+ if (changedProperties == null) return numUnchanged;
+ return getPropertyNames().size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
+ */
+ public Set<Name> getPropertyNames() {
+ if (changedProperties != null) {
+ Set<Name> result = new HashSet<Name>(original.getPropertyNames());
+ for (Map.Entry<Name, PropertyInfo> entry : changedProperties.entrySet()) {
+ if (entry.getValue() != DELETED_PROPERTY) {
+ result.add(entry.getKey());
+ } else {
+ result.remove(entry.getKey());
+ }
+ }
+ return result; // don't make unmod wrapper, since we've already made a copy ...
+ }
+ return original.getPropertyNames();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getProperty(org.jboss.dna.graph.property.Name)
+ */
+ public PropertyInfo getProperty( Name name ) {
+ if (changedProperties != null && changedProperties.containsKey(name)) {
+ return changedProperties.get(name); // either the changed PropertyInfo, or null if property was deleted
+ }
+ return original.getProperty(name);
+ }
+
+ public PropertyInfo setProperty( PropertyInfo newProperty ) {
+ Name name = newProperty.getPropertyName();
+ if (changedProperties == null) {
+ // There were no changes made yet ...
+
+ // Create the map of changed properties ...
+ changedProperties = new HashMap<Name, PropertyInfo>();
+ changedProperties.put(name, newProperty);
+
+ // And return the original property (or null if there was none) ...
+ return original.getProperty(name);
+ }
+ // The property may already have been changed, in which case we need to return the changed one ...
+ if (changedProperties.containsKey(name)) {
+ PropertyInfo changed = changedProperties.put(name, null);
+ // The named property was indeed deleted ...
+ return changed;
+ }
+ // Otherwise, the property was not yet changed or deleted ...
+ PropertyInfo changed = original.getProperty(name);
+ changedProperties.put(name, newProperty);
+ return changed;
+ }
+
+ public PropertyInfo removeProperty( Name name ) {
+ if (changedProperties == null) {
+ // Make sure the property was in the original ...
+ PropertyInfo existing = original.getProperty(name);
+ if (existing == null) {
+ // The named property didn't exist in the original, nor was it added and deleted in this object ...
+ return null;
+ }
+
+ // Create the map of changed properties ...
+ changedProperties = new HashMap<Name, PropertyInfo>();
+ changedProperties.put(name, DELETED_PROPERTY);
+ return existing;
+ }
+ // The property may already have been changed, in which case we need to return the changed one ...
+ if (changedProperties.containsKey(name)) {
+ PropertyInfo changed = changedProperties.put(name, null);
+ // The named property was indeed deleted ...
+ return changed;
+ }
+ // Otherwise, the property was not yet changed or deleted ...
+ PropertyInfo changed = original.getProperty(name);
+ changedProperties.put(name, null);
+ return changed;
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedNodeInfo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -29,13 +29,19 @@
import org.jboss.dna.graph.property.Path;
/**
- *
+ * The representation of a child node. This is an immutable representation of a child node within the collection of its siblings
+ * as the collection appeared at some point in time. This should be used as a guide to determine how long to hold onto references.
+ * <p>
+ * For example, adding and removing children may affect the {@link #getSnsIndex() same-name-sibling index} of the children, so
+ * these kinds of operations will result in the replacement of old ChildObject instances. Therefore, clients should generally find
+ * the ChildNode instances in a {@link Children} container, use the ChildNode objects quickly, then discard their references.
+ * </p>
+ * <p>
+ * There may be times when a client does wish to keep a representation of a ChildNode as it appeared at some moment in time, and
+ * so it may want to hold onto references to ChildNode objects for longer durations. This is fine, as long as it is understood
+ * that at some point the referenced ChildNode may no longer represent the current state.
+ * </p>
*/
-/**
- * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded. Clients
- * should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects and
- * replacing them with new instances.
- */
@Immutable
public final class ChildNode {
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -35,7 +35,7 @@
* An immutable implementation of {@link Children}.
*/
@Immutable
-public final class EmptyChildren implements Children {
+public final class EmptyChildren implements Children, InternalChildren {
static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
@@ -100,12 +100,10 @@
}
/**
- * Create another Children object that is equivalent to this node but with the supplied child added.
+ * {@inheritDoc}
*
- * @param newChildName the name of the new child; may not be null
- * @param newChildUuid the UUID of the new child; may not be null
- * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
- * @return the new Children object; never null
+ * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+ * org.jboss.dna.graph.property.PathFactory)
*/
public ChangedChildren with( Name newChildName,
UUID newChildUuid,
@@ -116,14 +114,11 @@
}
/**
- * Create another Children object that is equivalent to this node but without the supplied child. If the supplied child is not
- * a current child, this method silently returns this same instance (since it has not changed).
+ * {@inheritDoc}
*
- * @param child the child to be removed; may not be null
- * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
- * @return the new Children object; never null
+ * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
*/
- public ChangedChildren without( ChildNode child,
+ public ChangedChildren without( UUID childUuid,
PathFactory pathFactory ) {
return new ChangedChildren(this.parentUuid);
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -42,7 +42,7 @@
* An immutable implementation of {@link Children}.
*/
@Immutable
-public class ImmutableChildren implements Children {
+public class ImmutableChildren implements Children, InternalChildren {
protected final UUID parentUuid;
protected final Map<UUID, ChildNode> childrenByUuid;
protected final ListMultimap<Name, ChildNode> childrenByName;
@@ -168,12 +168,10 @@
}
/**
- * Create another Children object that is equivalent to this node but with the supplied child added.
+ * {@inheritDoc}
*
- * @param newChildName the name of the new child; may not be null
- * @param newChildUuid the UUID of the new child; may not be null
- * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
- * @return the new Children object; never null
+ * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+ * org.jboss.dna.graph.property.PathFactory)
*/
public ChangedChildren with( Name newChildName,
UUID newChildUuid,
@@ -184,19 +182,17 @@
}
/**
- * Create another Children object that is equivalent to this node but without the supplied child.
+ * {@inheritDoc}
*
- * @param child the child to be removed; may not be null
- * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
- * @return the new Children object; never null
+ * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory)
*/
- public ChangedChildren without( ChildNode child,
+ public ChangedChildren without( UUID childUuid,
PathFactory pathFactory ) {
- if (this.childrenByUuid.containsKey(child.getUuid()) && this.size() == 1) {
+ if (this.childrenByUuid.containsKey(childUuid) && this.size() == 1) {
return new ChangedChildren(this.parentUuid);
}
ChangedChildren newChildren = new ChangedChildren(this);
- return newChildren.without(child, pathFactory);
+ return newChildren.without(childUuid, pathFactory);
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -138,6 +138,15 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyCount()
+ */
+ public int getPropertyCount() {
+ return this.properties.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
*/
public Set<Name> getPropertyNames() {
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,57 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.UUID;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * An internal interface for the {@link Children} implementations. Methods on this interface are used internally and should not be
+ * used by components or clients.
+ */
+interface InternalChildren extends Children {
+
+ /**
+ * Create another Children object that is equivalent to this node but with the supplied child added.
+ *
+ * @param newChildName the name of the new child; may not be null
+ * @param newChildUuid the UUID of the new child; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ ChangedChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory );
+
+ /**
+ * Create another Children object that is equivalent to this node but without the supplied child.
+ *
+ * @param childUuid the UUID of the child to be removed; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ ChangedChildren without( UUID childUuid,
+ PathFactory pathFactory );
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/InternalChildren.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -30,7 +30,7 @@
import org.jboss.dna.jcr.NodeDefinitionId;
/**
- * The information that describes a node. This is the information that is kept in the cache.
+ * A representation of a node. This is the information that is kept in the cache.
*/
public interface NodeInfo {
@@ -60,7 +60,9 @@
public NodeDefinitionId getDefinitionId();
/**
- * Get the children for this node.
+ * Get the children for this node. Generally, clients should not hold onto the returned object but instead should simply use
+ * it and discard the reference. This is because implementations are not required to return the same instance with each call
+ * (although immutable implementations are expected to always return the same instance).
*
* @return the immutable children; never null but possibly empty
*/
@@ -74,6 +76,13 @@
public boolean hasProperties();
/**
+ * Return the number of properties on this node.
+ *
+ * @return the number of properties; never negative
+ */
+ public int getPropertyCount();
+
+ /**
* Get the names of the properties that are owned by this node.
*
* @return the unmodifiable set of property names
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -78,12 +78,12 @@
@Test
public void shouldReturnSameInstanceFromWithoutIfSuppliedChildIsNotFound() {
ChildNode nonExistant = new ChildNode(UUID.randomUUID(), pathFactory.createSegment("some segment"));
- assertThat(children.without(nonExistant, pathFactory), is(sameInstance((Children)children)));
+ assertThat(children.without(nonExistant.getUuid(), pathFactory), is(sameInstance((Children)children)));
}
@Test
public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
- Children newChildren = children.without(firstChild, pathFactory);
+ Children newChildren = children.without(firstChild.getUuid(), pathFactory);
assertThat(newChildren.size(), is(0));
}
@@ -113,7 +113,7 @@
assertChildNodesWithName(children, "childA", firstChild, child4, child5);
// Remove 'child4' ...
- Children result = children.without(child4, pathFactory);
+ Children result = children.without(child4.getUuid(), pathFactory);
// but the result should not have child4 ...
assertChildNodesWithName(result, "childA", firstChild, "childA");
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,629 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingNames.hasChild;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingNames.hasChildren;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingUuids.hasChild;
+import static org.jboss.dna.jcr.cache.IsNodeInfoWithChildrenHavingUuids.hasChildren;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.jcr.NodeDefinitionId;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ChangedNodeInfoTest {
+
+ private ExecutionContext context;
+ private PathFactory pathFactory;
+ private NodeInfo original;
+ private UUID uuid;
+ private Name primaryTypeName;
+ private Location location;
+ private NodeDefinitionId definitionId;
+ private ChangedChildren children;
+ private Map<Name, PropertyInfo> properties;
+ private ChangedNodeInfo changes;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ context.getNamespaceRegistry().register("acme", "http://example.com/acme");
+ pathFactory = context.getValueFactories().getPathFactory();
+
+ // Set up the original ...
+ uuid = UUID.randomUUID();
+ location = Location.create(uuid);
+ primaryTypeName = name("acme:geniusType");
+ definitionId = new NodeDefinitionId(name("acme:geniusContainerType"), name("acme:geniuses"));
+ children = new ChangedChildren(uuid);
+ properties = new HashMap<Name, PropertyInfo>();
+ original = new ImmutableNodeInfo(location, primaryTypeName, definitionId, uuid, children, properties);
+
+ // Create the changed node representation ...
+ changes = new ChangedNodeInfo(original);
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Segment segment( String segment ) {
+ return pathFactory.createSegment(segment);
+ }
+
+ /**
+ * Utility to set a property to the original node representation. This will replace any existing property with the same name.
+ *
+ * @param name the name of the property; may not be null
+ * @return the new property representation; never null
+ */
+ protected PropertyInfo makePropertyInOriginal( String name ) {
+ Name propName = name(name);
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ stub(propertyInfo.getPropertyName()).toReturn(propName);
+ properties.put(propName, propertyInfo);
+ return propertyInfo;
+ }
+
+ /**
+ * Utility to change a property in the changed representation.
+ *
+ * @param name the name of the property to change; may not be null
+ * @return the new property; never null
+ */
+ protected PropertyInfo setPropertyInChanged( String name ) {
+ Name propName = name(name);
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ stub(propertyInfo.getPropertyName()).toReturn(propName);
+ changes.setProperty(propertyInfo);
+ return propertyInfo;
+ }
+
+ protected ChildNode makeChildInOriginal( String childName ) {
+ ChildNode newChild = children.add(name(childName), UUID.randomUUID(), pathFactory);
+ return newChild;
+ }
+
+ protected ChildNode addChildInChanged( String childName ) {
+ ChildNode newChild = changes.addChild(name(childName), UUID.randomUUID(), pathFactory);
+ // Make sure that the 'changes' object no longer returns the original object's children object
+ assertThat(changes.getChildren(), is(not(sameInstance(original.getChildren()))));
+ return newChild;
+ }
+
+ protected void removeChildFromChanged( ChildNode child ) {
+ // Verify that a child node with the supplied UUID is contained.
+ // Note that it may not be the same ChildNode instance if another SNS node with smaller index was removed
+ assertThat(changes.getChildren().getChild(child.getUuid()), is(notNullValue()));
+ // Now remove the child, making sure the result is the same as the next 'getChildren()' call ...
+ assertThat(changes.removeChild(child.getUuid(), pathFactory), is(sameInstance(changes.getChildren())));
+ // Verify it no longer exists ...
+ assertThat(changes.getChildren().getChild(child.getUuid()), is(nullValue()));
+ }
+
+ @Test
+ public void shouldHaveLocationFromOriginal() {
+ assertThat(changes.getOriginalLocation(), is(sameInstance(location)));
+ }
+
+ @Test
+ public void shouldHaveParentUuidFromOriginal() {
+ assertThat(changes.getParent(), is(sameInstance(uuid)));
+ }
+
+ @Test
+ public void shouldHavePrimaryTypeNameFromOriginal() {
+ assertThat(changes.getPrimaryTypeName(), is(sameInstance(primaryTypeName)));
+ }
+
+ @Test
+ public void shouldHaveNodeDefinitionIdFromOriginal() {
+ assertThat(changes.getDefinitionId(), is(sameInstance(definitionId)));
+ }
+
+ @Test
+ public void shouldHaveChildrenFromOriginal() {
+ assertThat(changes.getChildren(), is(sameInstance((Children)children)));
+
+ ChildNode childA = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(1));
+ assertThat(changes.getChildren().getChild(childA.getUuid()), is(sameInstance(childA)));
+ assertThat(changes.getChildren(), hasChild(segment("childA[1]")));
+ assertThat(changes.getChildren(), hasChild(childA.getUuid()));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingChild() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add child ...
+ ChildNode childA3 = addChildInChanged("childA");
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(4));
+ assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+ segment("childB[1]"),
+ segment("childA[2]"),
+ segment("childA[3]")));
+ assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid(), childA3.getUuid()));
+ assertThat(changes.getChildren(), hasItems(childA1, childB1, childA2, childA3));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingMultipleChildren() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(6));
+ assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+ segment("childB[1]"),
+ segment("childA[2]"),
+ segment("childA[3]"),
+ segment("childC[1]"),
+ segment("childC[2]")));
+ assertThat(changes.getChildren(), hasChildren(childA1.getUuid(),
+ childB1.getUuid(),
+ childA2.getUuid(),
+ childA3.getUuid(),
+ childC1.getUuid(),
+ childC2.getUuid()));
+ assertThat(changes.getChildren(), hasItems(childA1, childB1, childA2, childA3, childC1, childC2));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingMultipleChildrenAndRemovingOthers() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Delete a child that was added and another that was an original ...
+ removeChildFromChanged(childC1);
+ removeChildFromChanged(childA2);
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(4));
+ assertThat(changes.getChildren(), hasChildren(segment("childA[1]"),
+ segment("childB[1]"),
+ segment("childA[2]"),
+ segment("childC[1]")));
+ assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA3.getUuid(), childC2.getUuid()));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingMultipleChildrenAndThenRemovingThoseJustAdded() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Delete a child that was added and another that was an original...
+ removeChildFromChanged(childA3);
+ removeChildFromChanged(childC1); // causes replacement of 'childC2' with lower SNS index
+ removeChildFromChanged(childC2);
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(3));
+ assertThat(changes.getChildren(), hasChildren(segment("childA[1]"), segment("childB[1]"), segment("childA[2]")));
+ assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid()));
+
+ // Do it again, but change the order of delete to delete from the back ...
+
+ // Add some children in the changed representation ...
+ childA3 = addChildInChanged("childA");
+ childC1 = addChildInChanged("childC");
+ childC2 = addChildInChanged("childC");
+
+ // Delete a child that was added and another that was an original ...
+ removeChildFromChanged(childC2);
+ removeChildFromChanged(childC1);
+ removeChildFromChanged(childA3);
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(3));
+ assertThat(changes.getChildren(), hasChildren(segment("childA[1]"), segment("childB[1]"), segment("childA[2]")));
+ assertThat(changes.getChildren(), hasChildren(childA1.getUuid(), childB1.getUuid(), childA2.getUuid()));
+
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterDeletingChild() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Delete a child that was added and another that was an original ...
+ removeChildFromChanged(childA1);
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(2));
+ assertThat(changes.getChildren(), hasChildren(segment("childB[1]"), segment("childA[1]")));
+ assertThat(changes.getChildren(), hasChildren(childB1.getUuid(), childA2.getUuid()));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterDeletingMultipleChildren() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Delete a child that was added and another that was an original ...
+ removeChildFromChanged(childA1);
+ removeChildFromChanged(childA2);
+
+ // Verify that all children are there in the proper order ...
+ assertThat(changes.getChildren().size(), is(1));
+ assertThat(changes.getChildren(), hasChildren(segment("childB[1]")));
+ assertThat(changes.getChildren(), hasChildren(childB1.getUuid()));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterDeletingAllChildrenFromTheFirsttChildToTheLast() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Delete all children, from the front to the back ...
+ removeChildFromChanged(childA1); // causes replacement of 'childA2' with lower SNS index
+ removeChildFromChanged(childA2);
+ removeChildFromChanged(childB1);
+
+ // Verify that all children have been removed ...
+ assertThat(changes.getChildren().size(), is(0));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterDeletingAllChildrenFromTheLastChildToTheFirst() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Delete all children, from the back to the front ...
+ removeChildFromChanged(childA2);
+ removeChildFromChanged(childB1);
+ removeChildFromChanged(childA1);
+
+ // Verify that all children have been removed ...
+ assertThat(changes.getChildren().size(), is(0));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingSomeChildrenThenDeletingAllChildrenFromTheFirstChildToTheLast() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Delete all children, from the front to the back ...
+ removeChildFromChanged(childA3);
+ removeChildFromChanged(childC1); // causes replacement of 'childC2' with lower SNS index
+ removeChildFromChanged(childC2);
+ removeChildFromChanged(childA1); // causes replacement of 'childA2' with lower SNS index
+ removeChildFromChanged(childB1);
+ removeChildFromChanged(childA2);
+
+ // Verify that all children have been removed ...
+ assertThat(changes.getChildren().size(), is(0));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterAddingSomeChildrenThenDeletingAllChildrenFromTheLastChildToTheFirst() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Delete all children, from the back to the front ...
+ removeChildFromChanged(childC2);
+ removeChildFromChanged(childC1);
+ removeChildFromChanged(childA3);
+ removeChildFromChanged(childA2);
+ removeChildFromChanged(childB1);
+ removeChildFromChanged(childA1);
+
+ // Verify that all children have been removed ...
+ assertThat(changes.getChildren().size(), is(0));
+ }
+
+ @Test
+ public void shouldHaveChildrenAfterReorderingChildren() {
+
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldNotRemoveFromNodeInfoWithNoChildChangesAChildThatMatchesSegmentButNotUuid() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
+ Children before = changes.getChildren();
+ int beforeSize = before.size();
+ Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+ assertThat(after.size(), is(beforeSize));
+ assertThat(after, is(sameInstance(before)));
+ assertThat(after, is(sameInstance(changes.getChildren())));
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldNotRemoveFromNodeInfoWithSomeChildChangesAChildThatMatchesSegmentButNotUuid() {
+ // Set up the original ...
+ ChildNode childA1 = makeChildInOriginal("childA");
+ ChildNode childB1 = makeChildInOriginal("childB");
+ ChildNode childA2 = makeChildInOriginal("childA");
+ assertThat(changes.getChildren().size(), is(3));
+
+ // Add some children in the changed representation ...
+ ChildNode childA3 = addChildInChanged("childA");
+ ChildNode childC1 = addChildInChanged("childC");
+ ChildNode childC2 = addChildInChanged("childC");
+
+ // Create a bogus node that has a new UUID but with the same segment as 'childA3' ...
+ Children before = changes.getChildren();
+ int beforeSize = before.size();
+ Children after = changes.removeChild(UUID.randomUUID(), pathFactory);
+ assertThat(after.size(), is(beforeSize));
+ assertThat(after, is(sameInstance(before)));
+ assertThat(after, is(sameInstance(changes.getChildren())));
+ }
+
+ @Test
+ public void shouldFindPropertyThatHasNotBeenModifiedButIsInOriginal() {
+ PropertyInfo propertyInfo = makePropertyInOriginal("test");
+ assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+ }
+
+ @Test
+ public void shouldFindPropertyThatHasBeenModifiedFromOriginal() {
+ PropertyInfo propertyInfo = makePropertyInOriginal("test");
+ assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+
+ // Modify the property ...
+ PropertyInfo newPropertyInfo = mock(PropertyInfo.class);
+ stub(newPropertyInfo.getPropertyName()).toReturn(name("test"));
+ PropertyInfo previous = changes.setProperty(newPropertyInfo);
+ assertThat(previous, is(sameInstance(propertyInfo)));
+
+ // Verify we can find the new property ...
+ assertThat(changes.getProperty(name("test")), is(sameInstance(newPropertyInfo)));
+ }
+
+ @Test
+ public void shouldNotFindPropertyThatHasBeenDeletedFromOriginal() {
+ PropertyInfo propertyInfo = makePropertyInOriginal("test");
+ assertThat(changes.getProperty(name("test")), is(sameInstance(propertyInfo)));
+
+ // Delete the property ...
+ PropertyInfo previous = changes.removeProperty(name("test"));
+ assertThat(previous, is(sameInstance(propertyInfo)));
+
+ // Verify we can not find the new property ...
+ assertThat(changes.getProperty(name("test")), is(nullValue()));
+ }
+
+ @Test
+ public void shouldNotFindPropertyThatIsNotInOriginal() {
+ assertThat(changes.getProperty(name("test")), is(nullValue()));
+
+ makePropertyInOriginal("test");
+ assertThat(changes.getProperty(name("nonExistant")), is(nullValue()));
+ }
+
+ @Test
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasNoChangedProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(4));
+ assertThat(names, hasItems(propA.getPropertyName(),
+ propB.getPropertyName(),
+ propC.getPropertyName(),
+ propD.getPropertyName()));
+ }
+
+ @Test
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasDeletedAllProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Remove all properties ...
+ assertThat(changes.removeProperty(propA.getPropertyName()), is(sameInstance(propA)));
+ assertThat(changes.removeProperty(propB.getPropertyName()), is(sameInstance(propB)));
+ assertThat(changes.removeProperty(propC.getPropertyName()), is(sameInstance(propC)));
+ assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(0));
+ }
+
+ @Test
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasDeletedSomeProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Remove some properties ...
+ assertThat(changes.removeProperty(propB.getPropertyName()), is(sameInstance(propB)));
+ assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(2));
+ assertThat(names, hasItems(propA.getPropertyName(), propC.getPropertyName()));
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedSomeProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Change some properties ...
+ PropertyInfo propB2 = setPropertyInChanged("propB");
+ PropertyInfo propC2 = setPropertyInChanged("propC");
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(4));
+ assertThat(names, hasItems(propA.getPropertyName(),
+ propB2.getPropertyName(),
+ propC2.getPropertyName(),
+ propD.getPropertyName()));
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasAddedSomeProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Add some properties ...
+ PropertyInfo propE = setPropertyInChanged("propE");
+ PropertyInfo propF = setPropertyInChanged("propF");
+ PropertyInfo propG = setPropertyInChanged("propG");
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(7));
+ assertThat(names, hasItems(propA.getPropertyName(),
+ propB.getPropertyName(),
+ propC.getPropertyName(),
+ propD.getPropertyName(),
+ propE.getPropertyName(),
+ propF.getPropertyName()));
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedAndDeletedAndAddedSomeProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Change some properties ...
+ PropertyInfo propB2 = setPropertyInChanged("propB");
+ PropertyInfo propC2 = setPropertyInChanged("propC");
+ // Add some properties ...
+ PropertyInfo propE = setPropertyInChanged("propE");
+ // Remove some properties ...
+ assertThat(changes.removeProperty(propB2.getPropertyName()), is(sameInstance(propB2)));
+ assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(3));
+ assertThat(names, hasItems(propA.getPropertyName(), propC2.getPropertyName(), propE.getPropertyName()));
+ }
+
+ @Test
+ @SuppressWarnings( "unused" )
+ public void shouldFindAllPropertyNamesWhenChangedNodeHasChangedAndDeletedAllProperties() {
+ PropertyInfo propA = makePropertyInOriginal("propA");
+ PropertyInfo propB = makePropertyInOriginal("propB");
+ PropertyInfo propC = makePropertyInOriginal("propC");
+ PropertyInfo propD = makePropertyInOriginal("propD");
+ // Change some properties ...
+ PropertyInfo propB2 = setPropertyInChanged("propB");
+ PropertyInfo propC2 = setPropertyInChanged("propC");
+ // Remove all properties ...
+ assertThat(changes.removeProperty(propA.getPropertyName()), is(sameInstance(propA)));
+ assertThat(changes.removeProperty(propB2.getPropertyName()), is(sameInstance(propB2)));
+ assertThat(changes.removeProperty(propC2.getPropertyName()), is(sameInstance(propC2)));
+ assertThat(changes.removeProperty(propD.getPropertyName()), is(sameInstance(propD)));
+ // Now ask for names
+ Set<Name> names = changes.getPropertyNames();
+ assertThat(names.size(), is(0));
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedNodeInfoTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java 2009-03-18 04:38:44 UTC (rev 781)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -76,7 +76,7 @@
@Test
public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
- Children newChildren = children.without(firstChild, pathFactory);
+ Children newChildren = children.without(firstChild.getUuid(), pathFactory);
assertThat(newChildren.size(), is(0));
}
@@ -93,7 +93,7 @@
assertChildNodesWithName(children, "childA", firstChild, child4, child5);
// Remove 'child4' ...
- Children result = children.without(child4, pathFactory);
+ Children result = children.without(child4.getUuid(), pathFactory);
// the original should not have been changed ...
assertChildNodesWithName(children, "childA", firstChild, child4, child5);
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,84 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.basic.BasicPathSegment;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * A JUnit {@link TypeSafeMatcher matcher} that can be used to assert that a NodeInfo (or other
+ * <code>Iterable<ChildNode></code>) has {@link ChildNode} instances with specific {@link ChildNode#getSegment() names}.
+ */
+public class IsNodeInfoWithChildrenHavingNames extends TypeSafeMatcher<Children> {
+ private final Matcher<Iterable<Path.Segment>> childMatcher;
+
+ public IsNodeInfoWithChildrenHavingNames( Matcher<Iterable<Path.Segment>> childMatcher ) {
+ this.childMatcher = childMatcher;
+ }
+
+ @Override
+ public boolean matchesSafely( Children children ) {
+ List<Path.Segment> childSegments = new ArrayList<Path.Segment>(children.size());
+ for (ChildNode child : children) {
+ childSegments.add(child.getSegment());
+ }
+ return childMatcher.matches(childSegments);
+ }
+
+ public void describeTo( Description description ) {
+ description.appendText("children").appendDescriptionOf(childMatcher);
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingNames hasChild( Name name,
+ int sameNameSiblingIndex ) {
+ Path.Segment segment = new BasicPathSegment(name, sameNameSiblingIndex);
+ return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItem(segment));
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingNames hasChild( Path.Segment child ) {
+ return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItem(child));
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingNames hasChildren( Path.Segment... childSegments ) {
+ return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItems(childSegments));
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingNames isEmpty() {
+ Path.Segment[] childSegments = new Path.Segment[] {};
+ return new IsNodeInfoWithChildrenHavingNames(IsCollectionContaining.hasItems(childSegments));
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingNames.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java 2009-03-18 16:54:54 UTC (rev 782)
@@ -0,0 +1,75 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * A JUnit {@link TypeSafeMatcher matcher} that can be used to assert that a NodeInfo (or other
+ * <code>Iterable<ChildNode></code>) has {@link ChildNode} instances with specific {@link ChildNode#getUuid() UUIDs}.
+ */
+public class IsNodeInfoWithChildrenHavingUuids extends TypeSafeMatcher<Children> {
+ private final Matcher<Iterable<UUID>> childMatcher;
+
+ public IsNodeInfoWithChildrenHavingUuids( Matcher<Iterable<UUID>> childMatcher ) {
+ this.childMatcher = childMatcher;
+ }
+
+ @Override
+ public boolean matchesSafely( Children children ) {
+ List<UUID> childSegments = new ArrayList<UUID>(children.size());
+ for (ChildNode child : children) {
+ childSegments.add(child.getUuid());
+ }
+ return childMatcher.matches(childSegments);
+ }
+
+ public void describeTo( Description description ) {
+ description.appendText("children with UUIDs").appendDescriptionOf(childMatcher);
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingUuids hasChild( UUID uuid ) {
+ return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItem(uuid));
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingUuids hasChildren( UUID... uuids ) {
+ return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItems(uuids));
+ }
+
+ @Factory
+ public static IsNodeInfoWithChildrenHavingUuids isEmpty() {
+ UUID[] uuids = new UUID[] {};
+ return new IsNodeInfoWithChildrenHavingUuids(IsCollectionContaining.hasItems(uuids));
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/IsNodeInfoWithChildrenHavingUuids.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
15 years, 1 month
DNA SVN: r781 - trunk/dna-jcr/src/test/java/org/jboss/dna/jcr.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-18 00:38:44 -0400 (Wed, 18 Mar 2009)
New Revision: 781
Modified:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
Log:
DNA-304 SessionCache Uses Graph Layer Cardinality for Properties Instead of Cardinality from JCR Property Definition
Applied the changes to the test cases.
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-17 19:16:04 UTC (rev 780)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-18 04:38:44 UTC (rev 781)
@@ -48,6 +48,7 @@
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import javax.security.auth.Subject;
@@ -58,6 +59,7 @@
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySourceException;
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Path;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -101,6 +103,7 @@
graph.create("/a").and().create("/a/b").and().create("/a/b/c");
graph.set("booleanProperty").on("/a/b").to(true);
graph.set("stringProperty").on("/a/b/c").to("value");
+ graph.set("jcr:mixinTypes").on("/a/b").to("mix:referenceable");
graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
// Make sure the path to the namespaces exists ...
@@ -392,7 +395,32 @@
// assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
}
+ /**
+ * DNA JCR implementation is supposed to have root type named {@link DnaLexicon#ROOT}.
+ *
+ * @throws Exception if an error occurs during the test
+ */
@Test
+ public void rootNodeShouldHaveProperType() throws Exception {
+ Node rootNode = session.getRootNode();
+
+ assertTrue(rootNode.getPrimaryNodeType().equals(session.nodeTypeManager().getNodeType(DnaLexicon.ROOT)));
+
+ }
+
+ /**
+ * DNA JCR implementation is supposed to have a referenceable root.
+ *
+ * @throws RepositoryException if an error occurs during the test
+ */
+ @Test
+ public void rootNodeShouldBeReferenceable() throws RepositoryException {
+ Node rootNode = session.getRootNode();
+
+ assertTrue(rootNode.getPrimaryNodeType().isNodeType(JcrMixLexicon.REFERENCEABLE.getString(context.getNamespaceRegistry())));
+ }
+
+ @Test
public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
OutputStream os = new ByteArrayOutputStream();
@@ -401,4 +429,22 @@
String fileContents = os.toString();
assertTrue(fileContents.contains(MULTI_LINE_VALUE));
}
+
+ @Test
+ public void shouldUseJcrCardinalityPerPropertyDefinition() throws Exception {
+
+ // Verify that the node does exist in the source ...
+ Path pathToNode = context.getValueFactories().getPathFactory().create("/a/b");
+ Node carsNode = session.getNode(pathToNode);
+
+ String mixinTypesName = JcrLexicon.MIXIN_TYPES.getString(session.getExecutionContext().getNamespaceRegistry());
+ Property mixinTypes = carsNode.getProperty(mixinTypesName);
+
+ // Check that the underlying DNA property has one value - this is a test of the testing data
+ assertThat(((AbstractJcrProperty)mixinTypes).property().isMultiple(), is(false));
+ // Check that the JCR property is a MultiProperty - this call will throw an exception if the property is not.
+ mixinTypes.getValues();
+
+ }
+
}
15 years, 1 month
DNA SVN: r780 - in trunk/dna-jcr/src: main/java/org/jboss/dna/jcr/cache and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-17 15:16:04 -0400 (Tue, 17 Mar 2009)
New Revision: 780
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
Log:
DNA-194 Implement update JCR capability
Added mutable versions of the classes used to store node, property and children representations.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -62,6 +62,7 @@
import org.jboss.dna.graph.property.ValueFormatException;
import org.jboss.dna.jcr.cache.ChildNode;
import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.EmptyChildren;
import org.jboss.dna.jcr.cache.ImmutableChildren;
import org.jboss.dna.jcr.cache.ImmutableNodeInfo;
import org.jboss.dna.jcr.cache.NodeInfo;
@@ -131,7 +132,7 @@
private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
- private final HashMap<UUID, NodeInfo> cachedNodes;
+ private final HashMap<UUID, ImmutableNodeInfo> cachedNodes;
private final HashMap<UUID, NodeInfo> changedNodes;
public SessionCache( JcrSession session,
@@ -159,7 +160,7 @@
this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
- this.cachedNodes = new HashMap<UUID, NodeInfo>();
+ this.cachedNodes = new HashMap<UUID, ImmutableNodeInfo>();
this.changedNodes = new HashMap<UUID, NodeInfo>();
}
@@ -554,9 +555,10 @@
// Now process all of the nodes that we loaded, again starting at the top and going down ...
for (Path batchPath : pathsInBatch) {
org.jboss.dna.graph.Node dnaNode = batchResults.getNode(batchPath);
- childInfo = createNodeInfoFrom(dnaNode, info);
- this.cachedNodes.put(childInfo.getUuid(), childInfo);
- info = childInfo;
+ ImmutableNodeInfo originalChildInfo = createNodeInfoFrom(dnaNode, info);
+ this.cachedNodes.put(originalChildInfo.getUuid(), originalChildInfo);
+ childInfo = originalChildInfo;
+ info = originalChildInfo;
}
} else {
// This is the last path, so do it a little more efficiently than above ...
@@ -651,12 +653,12 @@
* @throws ItemNotFoundException if the node does not exist in the repository
* @throws RepositoryException if there was an error obtaining this information from the repository
*/
- protected NodeInfo loadFromGraph( UUID uuid,
- NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
+ protected ImmutableNodeInfo loadFromGraph( UUID uuid,
+ NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
// Load the node information from the store ...
try {
org.jboss.dna.graph.Node node = store.getNodeAt(uuid);
- NodeInfo info = createNodeInfoFrom(node, parentInfo);
+ ImmutableNodeInfo info = createNodeInfoFrom(node, parentInfo);
this.cachedNodes.put(info.getUuid(), info);
return info;
} catch (org.jboss.dna.graph.property.PathNotFoundException e) {
@@ -681,12 +683,12 @@
* @throws PathNotFoundException if the node does not exist in the repository
* @throws RepositoryException if there was an error obtaining this information from the repository
*/
- protected NodeInfo loadFromGraph( Path path,
- NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
+ protected ImmutableNodeInfo loadFromGraph( Path path,
+ NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
// Load the node information from the store ...
try {
org.jboss.dna.graph.Node node = store.getNodeAt(path);
- NodeInfo info = createNodeInfoFrom(node, parentInfo);
+ ImmutableNodeInfo info = createNodeInfoFrom(node, parentInfo);
this.cachedNodes.put(info.getUuid(), info);
return info;
} catch (org.jboss.dna.graph.property.PathNotFoundException e) {
@@ -706,8 +708,8 @@
* @throws RepositoryException if there is an error determining the child {@link NodeDefinition} for the supplied node,
* preventing the node information from being constructed
*/
- private NodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
- NodeInfo parentInfo ) throws RepositoryException {
+ private ImmutableNodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
+ NodeInfo parentInfo ) throws RepositoryException {
// Now get the DNA node's UUID and find the DNA property containing the UUID ...
Location location = graphNode.getLocation();
ValueFactories factories = context.getValueFactories();
@@ -953,7 +955,8 @@
// Create the node information ...
UUID parentUuid = parentInfo != null ? parentInfo.getUuid() : null;
- Children children = new ImmutableChildren(parentUuid, graphNode.getChildren());
+ List<Location> locations = graphNode.getChildren();
+ Children children = locations.isEmpty() ? new EmptyChildren(parentUuid) : new ImmutableChildren(parentUuid, locations);
props = Collections.unmodifiableMap(props);
return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
}
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,101 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.List;
+import java.util.ListIterator;
+import java.util.UUID;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A {@link NotThreadSafe non-thread safe} implementation of {@link Children} that can be modified in place. This is typically
+ * used to capture changes made within a session.
+ */
+@NotThreadSafe
+public class ChangedChildren extends ImmutableChildren {
+
+ public ChangedChildren( Children original ) {
+ super(original);
+ }
+
+ /**
+ * Creates an empty instance.
+ *
+ * @param parentUuid the UUID of the parent node
+ */
+ protected ChangedChildren( UUID parentUuid ) {
+ super(parentUuid);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.ImmutableChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID,
+ * org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public ChangedChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory ) {
+ // Simply add the node to this object ...
+ super.add(newChildName, newChildUuid, pathFactory);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.ImmutableChildren#without(org.jboss.dna.jcr.cache.ChildNode,
+ * org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public ChangedChildren without( ChildNode child,
+ PathFactory pathFactory ) {
+ // Make sure this object contains the child ...
+ if (!childrenByUuid.containsKey(child.getUuid())) {
+ return this;
+ }
+ // Remove the child fro this object, then adjust the remaining child node instances that follow it ...
+ Name childName = child.getName();
+ List<ChildNode> childrenWithSameName = childrenByName.get(childName);
+ int snsIndex = child.getSnsIndex();
+ if (snsIndex > childrenWithSameName.size()) {
+ // The child node (with that SNS index) is no longer here) ...
+ return this;
+ }
+ ListIterator<ChildNode> iter = childrenWithSameName.listIterator(--snsIndex);
+ assert iter.hasNext();
+ iter.next(); // start ...
+ iter.remove(); // removes the item that was last returned from 'next()'
+ while (iter.hasNext()) {
+ ChildNode next = iter.next();
+ ChildNode newNext = next.with(pathFactory.createSegment(childName, ++snsIndex));
+ iter.set(newNext);
+ }
+ return this;
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChangedChildren.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -27,7 +27,6 @@
import java.util.UUID;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.graph.property.PathFactory;
/**
* Class that maintains the ordered list of {@link ChildNode} instances yet allows fast access to the children with a specified
@@ -74,15 +73,4 @@
*/
Iterator<ChildNode> getChildren( Name name );
- /**
- * Create another Children object that is equivalent to this node but with the supplied child added.
- *
- * @param newChildName the name of the new child; may not be null
- * @param newChildUuid the UUID of the new child; may not be null
- * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
- * @return the new Children object; never null
- */
- Children with( Name newChildName,
- UUID newChildUuid,
- PathFactory pathFactory );
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -37,7 +37,7 @@
@Immutable
public final class EmptyChildren implements Children {
- private static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
+ static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
private final UUID parentUuid;
@@ -99,13 +99,36 @@
return EMPTY_ITERATOR;
}
- public ImmutableChildren with( Name newChildName,
- UUID newChildUuid,
- PathFactory pathFactory ) {
- return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+ /**
+ * Create another Children object that is equivalent to this node but with the supplied child added.
+ *
+ * @param newChildName the name of the new child; may not be null
+ * @param newChildUuid the UUID of the new child; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ public ChangedChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory ) {
+ ChangedChildren result = new ChangedChildren(this);
+ result.add(newChildName, newChildUuid, pathFactory);
+ return result;
}
/**
+ * Create another Children object that is equivalent to this node but without the supplied child. If the supplied child is not
+ * a current child, this method silently returns this same instance (since it has not changed).
+ *
+ * @param child the child to be removed; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ public ChangedChildren without( ChildNode child,
+ PathFactory pathFactory ) {
+ return new ChangedChildren(this.parentUuid);
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -42,16 +42,14 @@
* An immutable implementation of {@link Children}.
*/
@Immutable
-public final class ImmutableChildren implements Children {
- private final UUID parentUuid;
- private final Map<UUID, ChildNode> childrenByUuid;
- private final ListMultimap<Name, ChildNode> childrenByName;
+public class ImmutableChildren implements Children {
+ protected final UUID parentUuid;
+ protected final Map<UUID, ChildNode> childrenByUuid;
+ protected final ListMultimap<Name, ChildNode> childrenByName;
public ImmutableChildren( UUID parentUuid,
Iterable<Location> children ) {
- this.parentUuid = parentUuid;
- this.childrenByUuid = new HashMap<UUID, ChildNode>();
- this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+ this(parentUuid);
for (Location childLocation : children) {
UUID childUuid = childLocation.getUuid();
Path.Segment segment = childLocation.getPath().getLastSegment();
@@ -62,10 +60,13 @@
}
}
- protected ImmutableChildren( Children original,
- Name additionalChildName,
- UUID childUuid,
- PathFactory pathFactory ) {
+ public ImmutableChildren( UUID parentUuid ) {
+ this.parentUuid = parentUuid;
+ this.childrenByUuid = new HashMap<UUID, ChildNode>();
+ this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+ }
+
+ protected ImmutableChildren( Children original ) {
this.parentUuid = original.getParentUuid();
this.childrenByUuid = new HashMap<UUID, ChildNode>();
this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
@@ -75,11 +76,38 @@
this.childrenByName.put(child.getName(), child);
this.childrenByUuid.put(child.getUuid(), child);
}
+ }
+
+ protected ImmutableChildren( Children original,
+ Name additionalChildName,
+ UUID childUuid,
+ PathFactory pathFactory ) {
+ this(original);
+ add(additionalChildName, childUuid, pathFactory);
+ }
+
+ /**
+ * Utility method that adds a child with the supplied name. This method is not exposed publicly, ensuring that this class
+ * remains publicly immutable. Subclasses that use this method (in places other than constructors) will no longer be
+ * {@link Immutable immutable}.
+ *
+ * @param additionalChildName
+ * @param childUuid
+ * @param pathFactory
+ * @return the child node that was just added; never null
+ */
+ protected ChildNode add( Name additionalChildName,
+ UUID childUuid,
+ PathFactory pathFactory ) {
+ ChildNode existing = this.childrenByUuid.get(childUuid);
+ if (existing != null) return existing;
+
List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName);
Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1);
ChildNode additionalChild = new ChildNode(childUuid, segment);
this.childrenByName.put(additionalChildName, additionalChild);
this.childrenByUuid.put(childUuid, additionalChild);
+ return additionalChild;
}
/**
@@ -139,13 +167,39 @@
return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator());
}
- public ImmutableChildren with( Name newChildName,
- UUID newChildUuid,
- PathFactory pathFactory ) {
- return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+ /**
+ * Create another Children object that is equivalent to this node but with the supplied child added.
+ *
+ * @param newChildName the name of the new child; may not be null
+ * @param newChildUuid the UUID of the new child; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ public ChangedChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory ) {
+ // Create a mutable version ...
+ ChangedChildren newChildren = new ChangedChildren(this);
+ return newChildren.with(newChildName, newChildUuid, pathFactory);
}
/**
+ * Create another Children object that is equivalent to this node but without the supplied child.
+ *
+ * @param child the child to be removed; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ public ChangedChildren without( ChildNode child,
+ PathFactory pathFactory ) {
+ if (this.childrenByUuid.containsKey(child.getUuid()) && this.size() == 1) {
+ return new ChangedChildren(this.parentUuid);
+ }
+ ChangedChildren newChildren = new ChangedChildren(this);
+ return newChildren.without(child, pathFactory);
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -62,7 +62,7 @@
/**
* Get the children for this node.
*
- * @return the children; never null but possibly empty
+ * @return the immutable children; never null but possibly empty
*/
public Children getChildren();
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-17 15:45:11 UTC (rev 779)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -52,6 +52,7 @@
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.cache.ChangedChildren;
import org.jboss.dna.jcr.cache.Children;
import org.jboss.dna.jcr.cache.EmptyChildren;
import org.jboss.dna.jcr.cache.NodeInfo;
@@ -151,7 +152,11 @@
protected void addChild( Name childName,
UUID childUuid ) {
- children = children.with(childName, childUuid, context.getValueFactories().getPathFactory());
+ if (children instanceof EmptyChildren) {
+ children = ((EmptyChildren)children).with(childName, childUuid, context.getValueFactories().getPathFactory());
+ } else if (children instanceof ChangedChildren) {
+ children = ((ChangedChildren)children).with(childName, childUuid, context.getValueFactories().getPathFactory());
+ }
stub(info.getChildren()).toReturn(children);
}
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,168 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @param <ChildrenType> the type for the {@link #children} variable
+ */
+public abstract class AbstractChildrenTest<ChildrenType extends Children> {
+
+ protected ChildrenType children;
+ protected ExecutionContext context;
+ protected PathFactory pathFactory;
+ protected NameFactory nameFactory;
+ protected UUID parentUuid;
+ protected UUID firstChildUuid;
+ protected Name firstChildName;
+ protected ChildNode firstChild;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ pathFactory = context.getValueFactories().getPathFactory();
+ nameFactory = context.getValueFactories().getNameFactory();
+
+ parentUuid = UUID.randomUUID();
+ firstChildUuid = UUID.randomUUID();
+ firstChildName = nameFactory.create("childA");
+ }
+
+ protected Name name( String name ) {
+ return nameFactory.create(name);
+ }
+
+ protected void assertChildNodesWithName( Children children,
+ String childName,
+ Object... childNodes ) {
+ Name name = name(childName);
+ Iterator<ChildNode> iter = children.getChildren(name);
+ int expectedSnsIndex = 1;
+ for (Object expectedChild : childNodes) {
+ assertThat(iter.hasNext(), is(true));
+ ChildNode next = iter.next();
+ assertThat(next.getName(), is(name));
+ assertThat(next.getSnsIndex(), is(expectedSnsIndex++));
+ if (expectedChild instanceof ChildNode) {
+ assertThat(next, is(sameInstance(expectedChild)));
+ } else if (expectedChild instanceof Name) {
+ Name expectedName = (Name)expectedChild;
+ assertThat(next.getName(), is(expectedName));
+ } else if (expectedChild instanceof String) {
+ Name expectedName = name((String)expectedChild);
+ assertThat(next.getName(), is(expectedName));
+ }
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ protected void assertChildNodes( Children children,
+ Object... childNodes ) {
+ Iterator<ChildNode> iter = children.iterator();
+ Map<Name, AtomicInteger> expectedSnsIndexes = new HashMap<Name, AtomicInteger>();
+ for (Object expectedChild : childNodes) {
+ assertThat(iter.hasNext(), is(true));
+ ChildNode next = iter.next();
+ Name actualName = next.getName();
+ // Check the name ...
+ if (expectedChild instanceof ChildNode) {
+ assertThat(next, is(sameInstance(expectedChild)));
+ } else if (expectedChild instanceof Name) {
+ Name expectedName = (Name)expectedChild;
+ assertThat(actualName, is(expectedName));
+ } else if (expectedChild instanceof String) {
+ Name expectedName = name((String)expectedChild);
+ assertThat(actualName, is(expectedName));
+ }
+ // Check the SNS ...
+ AtomicInteger expectedSns = expectedSnsIndexes.get(actualName);
+ if (expectedSns == null) {
+ expectedSns = new AtomicInteger(1);
+ expectedSnsIndexes.put(actualName, expectedSns);
+ }
+ assertThat(next.getSnsIndex(), is(expectedSns.getAndIncrement()));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ protected void assertSameContent( Children children,
+ Children other ) {
+ Iterator<ChildNode> iter = children.iterator();
+ Iterator<ChildNode> otherIter = other.iterator();
+ while (iter.hasNext()) {
+ assertThat(otherIter.hasNext(), is(true));
+ ChildNode next = iter.next();
+ ChildNode otherNext = otherIter.next();
+ assertThat(next, is(otherNext));
+ }
+ assertThat(iter.hasNext(), is(false));
+ assertThat(otherIter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldFindFirstChildByUuid() {
+ ChildNode firstChild = children.getChild(firstChildUuid);
+ assertThat(firstChild, is(notNullValue()));
+ assertThat(firstChild, is(sameInstance(this.firstChild)));
+ assertThat(firstChild.getUuid(), is(firstChildUuid));
+ assertThat(firstChild.getName(), is(firstChildName));
+ assertThat(firstChild.getSnsIndex(), is(1));
+ assertThat(firstChild.getSegment().getIndex(), is(1));
+ assertThat(firstChild.getSegment().getName(), is(firstChildName));
+ }
+
+ @Test
+ public void shouldFindFirstChildByName() {
+ Segment segment = pathFactory.createSegment(firstChildName, 1);
+ ChildNode firstChild = children.getChild(segment);
+ assertThat(firstChild, is(notNullValue()));
+ assertThat(firstChild, is(sameInstance(this.firstChild)));
+ assertThat(firstChild.getUuid(), is(firstChildUuid));
+ assertThat(firstChild.getName(), is(firstChildName));
+ assertThat(firstChild.getSnsIndex(), is(1));
+ assertThat(firstChild.getSegment().getIndex(), is(1));
+ assertThat(firstChild.getSegment().getName(), is(firstChildName));
+ }
+
+ @Test
+ public void shouldImplementToString() {
+ children.toString();
+ }
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/AbstractChildrenTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,150 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ChangedChildrenTest extends AbstractChildrenTest<ChangedChildren> {
+
+ private ImmutableChildren original;
+
+ @Override
+ @Before
+ public void beforeEach() {
+ super.beforeEach();
+
+ original = new ImmutableChildren(parentUuid);
+ firstChild = original.add(firstChildName, firstChildUuid, pathFactory);
+
+ children = new ChangedChildren(original);
+ }
+
+ @Test
+ public void shouldHaveCorrectSize() {
+ assertThat(children.size(), is(1));
+ }
+
+ @Test
+ public void shouldHaveSameContentsAsOriginal() {
+ assertSameContent(children, original);
+ }
+
+ @Test
+ public void shouldFindChildrenByName() {
+ Iterator<ChildNode> iter = children.getChildren(firstChildName);
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(firstChild)));
+ assertThat(iter.hasNext(), is(false));
+ try {
+ iter.next();
+ fail("Failed to throw exception");
+ } catch (NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void shouldReturnSameInstanceFromWithoutIfSuppliedChildIsNotFound() {
+ ChildNode nonExistant = new ChildNode(UUID.randomUUID(), pathFactory.createSegment("some segment"));
+ assertThat(children.without(nonExistant, pathFactory), is(sameInstance((Children)children)));
+ }
+
+ @Test
+ public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
+ Children newChildren = children.without(firstChild, pathFactory);
+ assertThat(newChildren.size(), is(0));
+ }
+
+ @Test
+ public void shouldReturnSameInstanceFromWithIfSuppliedChildThatIsFoundInContainer() {
+ assertThat(children.with(firstChildName, firstChildUuid, pathFactory), is(sameInstance((Children)children)));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldReturnIteratorThatDoesNotSupportRemoving() {
+ Iterator<ChildNode> iter = children.getChildren(firstChildName);
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(firstChild)));
+ iter.remove();
+ }
+
+ @Test
+ public void shouldReturnChangedChildrenFromWithoutIfSuppliedChildIsFound() {
+ ChildNode child2 = children.add(name("childB"), UUID.randomUUID(), pathFactory);
+ ChildNode child3 = children.add(name("childC"), UUID.randomUUID(), pathFactory);
+ ChildNode child4 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+ ChildNode child5 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+ ChildNode child6 = children.add(name("childD"), UUID.randomUUID(), pathFactory);
+
+ // Check that the children contains what we expect ...
+ assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+ assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+
+ // Remove 'child4' ...
+ Children result = children.without(child4, pathFactory);
+
+ // but the result should not have child4 ...
+ assertChildNodesWithName(result, "childA", firstChild, "childA");
+
+ // Now check that all the child nodes are in the result, in the expected order ...
+ assertChildNodes(result, firstChild, child2, child3, "childA", child6);
+ }
+
+ @Test
+ public void shouldReturnChangedChildrenFromWithIfSuppliedChildIsNotFound() {
+ // Make sure that children contains what we expect ...
+ assertChildNodes(children, firstChild);
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add a node ...
+ Children result = children.with(name("childB"), UUID.randomUUID(), pathFactory);
+ assertThat(result, is(sameInstance((Children)children)));
+ assertChildNodes(children, firstChild, "childB");
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add another node ...
+ result = children.with(name("childC"), UUID.randomUUID(), pathFactory);
+ assertThat(result, is(sameInstance((Children)children)));
+ assertChildNodes(children, firstChild, "childB", "childC");
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add another node ...
+ result = children.with(name("childA"), UUID.randomUUID(), pathFactory);
+ assertThat(result, is(sameInstance((Children)children)));
+ assertChildNodes(children, firstChild, "childB", "childC", "childA");
+ assertChildNodesWithName(children, "childA", firstChild, "childA");
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ChangedChildrenTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java 2009-03-17 19:16:04 UTC (rev 780)
@@ -0,0 +1,141 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ImmutableChildrenTest extends AbstractChildrenTest<ImmutableChildren> {
+
+ @Override
+ @Before
+ public void beforeEach() {
+ super.beforeEach();
+
+ children = new ImmutableChildren(parentUuid);
+ firstChild = children.add(firstChildName, firstChildUuid, pathFactory);
+ }
+
+ @Test
+ public void shouldHaveCorrectSize() {
+ assertThat(children.size(), is(1));
+ }
+
+ @Test
+ public void shouldFindChildrenByName() {
+ Iterator<ChildNode> iter = children.getChildren(firstChildName);
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(firstChild)));
+ assertThat(iter.hasNext(), is(false));
+ try {
+ iter.next();
+ fail("Failed to throw exception");
+ } catch (NoSuchElementException e) {
+ // expected
+ }
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldReturnIteratorThatDoesNotSupportRemoving() {
+ Iterator<ChildNode> iter = children.getChildren(firstChildName);
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(firstChild)));
+ iter.remove();
+ }
+
+ @Test
+ public void shouldReturnEmptyChildrenFromWithoutIfOnlyChildIsRemoved() {
+ Children newChildren = children.without(firstChild, pathFactory);
+ assertThat(newChildren.size(), is(0));
+ }
+
+ @Test
+ public void shouldReturnChangedChildrenFromWithoutIfSuppliedChildIsFound() {
+ ChildNode child2 = children.add(name("childB"), UUID.randomUUID(), pathFactory);
+ ChildNode child3 = children.add(name("childC"), UUID.randomUUID(), pathFactory);
+ ChildNode child4 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+ ChildNode child5 = children.add(name("childA"), UUID.randomUUID(), pathFactory);
+ ChildNode child6 = children.add(name("childD"), UUID.randomUUID(), pathFactory);
+
+ // Check that the children contains what we expect ...
+ assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+ assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+
+ // Remove 'child4' ...
+ Children result = children.without(child4, pathFactory);
+
+ // the original should not have been changed ...
+ assertChildNodesWithName(children, "childA", firstChild, child4, child5);
+ // but the result should not have child4 ...
+ assertChildNodesWithName(result, "childA", firstChild, "childA");
+
+ // Now check that all the child nodes are still in the original, in the same order ...
+ assertChildNodes(children, firstChild, child2, child3, child4, child5, child6);
+
+ // Now check that all the child nodes are in the result, in the expected order ...
+ assertChildNodes(result, firstChild, child2, child3, "childA", child6);
+ }
+
+ @Test
+ public void shouldReturnChangedChildrenFromWithIfSuppliedChildIsNotFound() {
+ // Make sure that children contains what we expect ...
+ assertChildNodes(children, firstChild);
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add a node ...
+ ChangedChildren newChildren = children.with(name("childB"), UUID.randomUUID(), pathFactory);
+ assertChildNodes(newChildren, firstChild, "childB");
+ assertChildNodesWithName(newChildren, "childA", firstChild);
+ // And make sure 'children' hasn't changed ...
+ assertChildNodes(children, firstChild);
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add another node ...
+ newChildren = newChildren.with(name("childC"), UUID.randomUUID(), pathFactory);
+ assertChildNodes(newChildren, firstChild, "childB", "childC");
+ assertChildNodesWithName(newChildren, "childA", firstChild);
+ // And make sure 'children' hasn't changed ...
+ assertChildNodes(children, firstChild);
+ assertChildNodesWithName(children, "childA", firstChild);
+
+ // Add another node ...
+ newChildren = newChildren.with(name("childA"), UUID.randomUUID(), pathFactory);
+ assertChildNodes(newChildren, firstChild, "childB", "childC", "childA");
+ assertChildNodesWithName(newChildren, "childA", firstChild, "childA");
+ // And make sure 'children' hasn't changed ...
+ assertChildNodes(children, firstChild);
+ assertChildNodesWithName(children, "childA", firstChild);
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/cache/ImmutableChildrenTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
15 years, 1 month
DNA SVN: r779 - trunk/dna-jcr/src/main/java/org/jboss/dna/jcr.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-17 11:45:11 -0400 (Tue, 17 Mar 2009)
New Revision: 779
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
Log:
DNA-304 SessionCache Uses Graph Layer Cardinality for Properties Instead of Cardinality from JCR Property Definition
Applied the patch the corrected SessionCache to determine the multiplicity using the property definition rather than the number of values in the DNA property.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-17 15:43:09 UTC (rev 778)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-17 15:45:11 UTC (rev 779)
@@ -369,7 +369,7 @@
* @return the <i>new</i> instance of the {@link Property}; never null
*/
private AbstractJcrProperty createAndCacheJcrPropertyFor( PropertyInfo info ) {
- boolean multiValued = info.getProperty().isMultiple();
+ boolean multiValued = info.isMultiValued();
JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId(), multiValued);
assert definition != null;
if (multiValued) {
15 years, 1 month
DNA SVN: r778 - in trunk: dna-common/src/main/java/org/jboss/dna/common/util and 3 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-17 11:43:09 -0400 (Tue, 17 Mar 2009)
New Revision: 778
Added:
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java
trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java
Removed:
trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
Log:
DNA-194 Implement update JCR capability
Changed SessionCache to use immutable versions of the node, child and property representations, in preparation for mutable ones in the "changed cache".
Copied: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java (from rev 776, trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java)
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,61 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.collection;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * @author jverhaeg
+ * @param <T> some type
+ */
+public final class EmptyIterator<T> implements Iterator<T> {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#next()
+ */
+ public T next() {
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/EmptyIterator.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,70 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.collection;
+
+import java.util.Iterator;
+
+/**
+ * An {@link Iterator} implementation that only allows reading elements, used as a wrapper around another iterator to make the
+ * contents appear to be immutable.
+ *
+ * @param <T> the type that is being iterated over
+ */
+public final class ReadOnlyIterator<T> implements Iterator<T> {
+
+ private final Iterator<T> delegate;
+
+ public ReadOnlyIterator( Iterator<T> delegate ) {
+ this.delegate = delegate;
+ assert this.delegate != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#next()
+ */
+ public T next() {
+ return delegate.next();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+}
Property changes on: trunk/dna-common/src/main/java/org/jboss/dna/common/collection/ReadOnlyIterator.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Deleted: trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/EmptyIterator.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -1,61 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership. Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.common.util;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * @author jverhaeg
- * @param <T> some type
- */
-public final class EmptyIterator<T> implements Iterator<T> {
-
- /**
- * {@inheritDoc}
- *
- * @see java.util.Iterator#hasNext()
- */
- public boolean hasNext() {
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.util.Iterator#next()
- */
- public T next() {
- throw new NoSuchElementException();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.util.Iterator#remove()
- */
- public void remove() {
- throw new UnsupportedOperationException();
- }
-}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -56,14 +56,14 @@
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.ValueFormatException;
-import org.jboss.dna.jcr.SessionCache.ChildNode;
-import org.jboss.dna.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.cache.ChildNode;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.NodeInfo;
/**
* An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
- * {@link SessionCache}. Each instance references the {@link SessionCache.NodeInfo node information} also managed by the
- * SessionCache, and finds and operates against this information with each method call.
+ * {@link SessionCache}. Each instance references the {@link NodeInfo node information} also managed by the SessionCache, and
+ * finds and operates against this information with each method call.
*/
@Immutable
abstract class AbstractJcrNode extends AbstractJcrItem implements Node {
@@ -266,7 +266,7 @@
* @see javax.jcr.Node#hasProperties()
*/
public final boolean hasProperties() throws RepositoryException {
- return nodeInfo().getProperties().size() > 0;
+ return nodeInfo().hasProperties();
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -37,7 +37,7 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
/**
* @author jverhaeg
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -29,7 +29,7 @@
import javax.jcr.RepositoryException;
import net.jcip.annotations.Immutable;
import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.jcr.cache.ChildNode;
/**
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -25,15 +25,14 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
@@ -45,8 +44,6 @@
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
-import net.jcip.annotations.Immutable;
-import net.jcip.annotations.NotThreadSafe;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
@@ -63,9 +60,13 @@
import org.jboss.dna.graph.property.ValueFactories;
import org.jboss.dna.graph.property.ValueFactory;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.jcr.cache.ChildNode;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.ImmutableChildren;
+import org.jboss.dna.jcr.cache.ImmutableNodeInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import com.google.common.base.ReferenceType;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
import com.google.common.collect.ReferenceMap;
/**
@@ -247,10 +248,10 @@
public Collection<AbstractJcrProperty> findJcrPropertiesFor( UUID nodeUuid )
throws ItemNotFoundException, RepositoryException {
NodeInfo info = findNodeInfo(nodeUuid);
- Map<Name, PropertyInfo> properties = info.getProperties();
- Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(properties.size());
- for (PropertyInfo propertyInfo : properties.values()) {
- result.add(findJcrProperty(propertyInfo.getPropertyId()));
+ Set<Name> propertyNames = info.getPropertyNames();
+ Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(propertyNames.size());
+ for (Name propertyName : propertyNames) {
+ result.add(findJcrProperty(new PropertyId(nodeUuid, propertyName)));
}
return result;
}
@@ -377,22 +378,6 @@
return new JcrSingleValueProperty(this, info.getPropertyId());
}
- // public AbstractJcrProperty findJcrProperty( Path propertyPath ) throws PathNotFoundException, RepositoryException {
- // if (propertyPath.isRoot()) return null;
- // Path nodePath = propertyPath.getParent();
- // NodeInfo nodeInfo = findNodeInfo(nodePath);
- // Name propertyName = propertyPath.getLastSegment().getName();
- // PropertyInfo info = nodeInfo.getProperty(propertyName);
- //
- // // Look for an existing JCR Property object ...
- // PropertyId propertyId = info.getPropertyId();
- // AbstractJcrProperty property = jcrProperties.get(propertyId);
- // if (property != null) return property;
- //
- // // Now create the appropriate JCR Property object ...
- // return createAndCacheJcrPropertyFor(info);
- // }
-
/**
* Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
* especially after the information has been cached. Note, however, that this method first checks the cache, and if the
@@ -617,9 +602,7 @@
NodeInfo parentInfo = findNodeInfo(parent);
ChildNode child = parentInfo.getChildren().getChild(info.getUuid());
if (child == null) break;
- Path.Segment segment = pathFactory.createSegment(child.getName(), child.getSnsIndex());
-
- segments.addFirst(segment);
+ segments.addFirst(child.getSegment());
info = parentInfo;
}
return pathFactory.createAbsolutePath(segments);
@@ -826,17 +809,6 @@
}
}
- // Create the node information ...
- NodeInfo info = new NodeInfo(location, primaryTypeName, definition.getId());
- if (parentInfo != null) {
- info.setParent(parentInfo.getUuid());
- }
-
- // --------------------------------------------------
- // Set the node's children ...
- // --------------------------------------------------
- info.setChildren(graphNode.getChildren());
-
// ------------------------------------------------------
// Set the node's properties ...
// ------------------------------------------------------
@@ -894,6 +866,7 @@
}
// Now create the JCR property object wrappers around the other properties ...
+ Map<Name, PropertyInfo> props = new HashMap<Name, PropertyInfo>();
for (org.jboss.dna.graph.property.Property dnaProp : graphProperties.values()) {
Name name = dnaProp.getName();
@@ -958,7 +931,9 @@
// Record the property in the node information ...
PropertyId propId = new PropertyId(uuid, name);
- info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, propertyType, dnaProp);
+ JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+ PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), propertyType, dnaProp, defn.isMultiple());
+ props.put(name, propInfo);
}
// Now add the "jcr:uuid" property if and only if referenceable ...
@@ -966,15 +941,21 @@
// We know that this property is single-valued
PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
- info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, PropertyType.STRING, uuidProperty);
+ JcrPropertyDefinition defn = (JcrPropertyDefinition)propertyDefinition;
+ PropertyInfo propInfo = new PropertyInfo(propId, defn.getId(), PropertyType.STRING, uuidProperty, defn.isMultiple());
+ props.put(JcrLexicon.UUID, propInfo);
} else {
// Make sure there is NOT a "jcr:uuid" property ...
- info.removeProperty(JcrLexicon.UUID);
+ props.remove(JcrLexicon.UUID);
}
// Make sure the "dna:uuid" property did not get in there ...
- info.removeProperty(DnaLexicon.UUID);
+ props.remove(DnaLexicon.UUID);
- return info;
+ // Create the node information ...
+ UUID parentUuid = parentInfo != null ? parentInfo.getUuid() : null;
+ Children children = new ImmutableChildren(parentUuid, graphNode.getChildren());
+ props = Collections.unmodifiableMap(props);
+ return new ImmutableNodeInfo(location, primaryTypeName, definition.getId(), parentUuid, children, props);
}
/**
@@ -1001,583 +982,4 @@
// TODO: should this also check the mixins?
return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
}
-
- /**
- * The information that describes a node. This is the information that is kept in the {@link SessionCache#cachedNodes cache}
- * and in the record of {@link SessionCache#changedNodes changes} made by the session but not yet commited/saved.
- */
- @NotThreadSafe
- protected class NodeInfo {
- private final Location originalLocation;
- private final UUID uuid;
- private UUID parent;
- private final Name primaryTypeName;
- private final NodeDefinitionId definition;
- private final Children children;
- private final Map<Name, PropertyInfo> properties;
-
- protected NodeInfo( Location originalLocation,
- Name primaryTypeName,
- NodeDefinitionId definition ) {
- this.originalLocation = originalLocation;
- this.primaryTypeName = primaryTypeName;
- this.definition = definition;
- this.uuid = this.originalLocation.getUuid();
- this.children = new Children(this.uuid);
- this.properties = new HashMap<Name, PropertyInfo>();
- assert this.uuid != null;
- assert this.definition != null;
- assert this.primaryTypeName != null;
- }
-
- /**
- * @return location
- */
- public Location getOriginalLocation() {
- return originalLocation;
- }
-
- /**
- * @return uuid
- */
- public UUID getUuid() {
- return uuid;
- }
-
- /**
- * @return parent
- */
- public UUID getParent() {
- return parent;
- }
-
- /**
- * @param parent Sets parent to the specified value.
- */
- protected void setParent( UUID parent ) {
- this.parent = parent;
- }
-
- /**
- * @return primaryTypeName
- */
- public Name getPrimaryTypeName() {
- return primaryTypeName;
- }
-
- /**
- * @return definition
- */
- public NodeDefinitionId getDefinitionId() {
- return definition;
- }
-
- /**
- * Get the children for this node.
- *
- * @return the children; never null but possibly empty
- */
- public Children getChildren() {
- return children;
- }
-
- /**
- * @param children Sets children to the specified value.
- * @return the children information; never null
- */
- public Children setChildren( List<Location> children ) {
- this.children.append(SessionCache.this, children, true);
- return this.children;
- }
-
- /**
- * Get the map of information for each property.
- *
- * @return the information for each property; never null but possibly (though unlikely) empty
- */
- public Map<Name, PropertyInfo> getProperties() {
- return this.properties; // never null
- }
-
- public PropertyInfo getProperty( Name name ) {
- return this.properties.get(name);
- }
-
- public PropertyInfo setProperty( PropertyId id,
- JcrPropertyDefinition definition,
- int propertyType,
- Property dnaProperty ) {
- // Initialize the map if required (this never replaces it, though) ...
- PropertyInfo info = new PropertyInfo(id, definition.getId(), propertyType, dnaProperty, definition.isMultiple());
- return this.properties.put(id.getPropertyName(), info);
- }
-
- public PropertyInfo removeProperty( Name name ) {
- return this.properties.remove(name);
- }
- }
-
- /**
- * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
- * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
- * type}.
- * <p>
- * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an
- * instance by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their
- * reference. This is because these instances are replaced and discarded whenever anything about the property changes.
- * </p>
- */
- @Immutable
- public static class PropertyInfo {
- private final PropertyId propertyId;
- private final PropertyDefinitionId definitionId;
- private final Property dnaProperty;
- private final int propertyType;
- private final boolean multiValued;
-
- protected PropertyInfo( PropertyId propertyId,
- PropertyDefinitionId definitionId,
- int propertyType,
- Property dnaProperty,
- boolean multiValued ) {
- this.propertyId = propertyId;
- this.definitionId = definitionId;
- this.propertyType = propertyType;
- this.dnaProperty = dnaProperty;
- this.multiValued = multiValued;
- }
-
- /**
- * Get the durable identifier for this property.
- *
- * @return propertyId
- */
- public PropertyId getPropertyId() {
- return propertyId;
- }
-
- /**
- * Get the UUID of the node to which this property belongs.
- *
- * @return the owner node's UUID; never null
- */
- public UUID getNodeUuid() {
- return propertyId.getNodeId();
- }
-
- /**
- * The identifier for the property definition.
- *
- * @return the property definition ID; never null
- */
- public PropertyDefinitionId getDefinitionId() {
- return definitionId;
- }
-
- /**
- * Get the DNA Property, which contains the name and value(s)
- *
- * @return the property; never null
- */
- public Property getProperty() {
- return dnaProperty;
- }
-
- /**
- * Get the property name.
- *
- * @return the property name; never null
- */
- public Name getPropertyName() {
- return dnaProperty.getName();
- }
-
- /**
- * Get the JCR {@link PropertyType} for this property.
- *
- * @return the property type
- */
- public int getPropertyType() {
- return propertyType;
- }
-
- /**
- * @return multiValued
- */
- public boolean isMultiValued() {
- return multiValued;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- return propertyId.hashCode();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals( Object obj ) {
- if (obj == this) return true;
- if (obj instanceof PropertyInfo) {
- return propertyId.equals(((PropertyInfo)obj).getPropertyId());
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(propertyId);
- sb.append(" defined by ").append(definitionId);
- sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
- if (dnaProperty.isSingle()) {
- sb.append(" with value ");
- } else {
- sb.append(" with values ");
- }
- sb.append(dnaProperty.getValuesAsArray());
- return sb.toString();
- }
- }
-
- /**
- * Class that maintains the ordered list of {@link ChildNode} instances. This class uses a {@link ListMultimap} to maintain
- * insertion order of the child nodes, and to allow fast access to the children with a specified name.
- */
- @ThreadSafe
- public final static class Children implements Iterable<ChildNode> {
- private final UUID parentUuid;
- private final Map<UUID, ChildNode> childrenByUuid;
- private final ListMultimap<Name, ChildNode> childrenByName;
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- Children( UUID parentUuid ) {
- this.parentUuid = parentUuid;
- this.childrenByUuid = new HashMap<UUID, ChildNode>();
- this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
- }
-
- /**
- * Get the number of children.
- *
- * @return the number of children
- */
- public int size() {
- try {
- lock.readLock().lock();
- return childrenByName.size();
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Get the children.
- *
- * @return a copy of the list of children
- */
- public List<ChildNode> asList() {
- try {
- lock.readLock().lock();
- return new ArrayList<ChildNode>(childrenByName.values());
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Get the children.
- *
- * @return a copy of the list of children
- */
- public Iterator<ChildNode> iterator() {
- return asList().iterator();
- }
-
- /**
- * The UUID of the parent node.
- *
- * @return the parent node's UUID
- */
- public UUID getParentUuid() {
- return parentUuid;
- }
-
- /**
- * Get the child with the given UUID.
- *
- * @param uuid the UUID of the child node
- * @return the child node, or null if there is no child with the supplied UUID
- */
- public ChildNode getChild( UUID uuid ) {
- try {
- lock.readLock().lock();
- return this.childrenByUuid.get(uuid);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Get the child given the path segment.
- *
- * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
- * {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
- * @return the information for the child node, or null if no such child existed
- */
- public ChildNode getChild( Path.Segment segment ) {
- try {
- lock.readLock().lock();
- List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
- int snsIndex = segment.getIndex();
- if (childrenWithName.size() < snsIndex) return null;
- return childrenWithName.get(snsIndex - 1);
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Get the same-name-sibling children that all share the supplied name.
- *
- * @param name the name for the children; may not be null
- * @return the children with the supplied name; never null
- */
- public List<ChildNode> getChildren( Name name ) {
- try {
- lock.readLock().lock();
- return new ArrayList<ChildNode>(this.childrenByName.get(name));
- } finally {
- lock.readLock().unlock();
- }
- }
-
- /**
- * Remove the child with the given path segment.
- *
- * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
- * {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
- * @return the information for the child node that was removed, or null if no such child existed
- */
- public ChildNode remove( Path.Segment segment ) {
- try {
- lock.writeLock().lock();
- List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
- int snsIndex = segment.getIndex();
- int numChildrenWithName = childrenWithName.size();
- if (numChildrenWithName < snsIndex) return null;
- ChildNode result = childrenWithName.remove(snsIndex);
- this.childrenByUuid.remove(result.getUuid());
- --numChildrenWithName;
- if (numChildrenWithName > snsIndex) {
- // We need to reduce the SNS index of every child after the one that was just removed ...
- ListIterator<ChildNode> siblingIter = childrenWithName.listIterator(snsIndex);
- while (siblingIter.hasNext()) {
- // Remove the next child and replace with one having the correct SNS index ...
- ChildNode next = siblingIter.next();
- siblingIter.remove();
- siblingIter.set(next.withChangedSnsIndex(-1));
- }
- }
- return result;
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * Add a new child to the end of the list of existing children a new child with the supplied name and UUID. The
- * same-name-sibling will be determined to be one more than the number of existing children with the same name.
- *
- * @param cache the cache; may not be null
- * @param name the name for the child to be appended; may not be null
- * @param childUuid the UUID of the child; may not be null
- * @return the information for the newly-added child; never null
- */
- public ChildNode append( SessionCache cache,
- Name name,
- UUID childUuid ) {
- try {
- lock.writeLock().lock();
- List<ChildNode> childrenWithName = this.childrenByName.get(name);
- ChildNode child = new ChildNode(childUuid, name, childrenWithName.size() + 1);
- childrenWithName.add(child);
- this.childrenByUuid.put(childUuid, child);
- // Look for the child in the cache/changed nodes ...
- NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
- if (childInfo != null) childInfo.setParent(parentUuid);
- return child;
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * Append the children described by the supplied Location objects, optionally removing all existing children first.
- *
- * @param cache the cache; may not be null
- * @param children a list containing a Location object for each child
- * @param removeExistingFirst true if the existing children should be removed before these children are added, or false if
- * these children should be appended to the existing children
- */
- public void append( SessionCache cache,
- List<Location> children,
- boolean removeExistingFirst ) {
- try {
- lock.writeLock().lock();
- if (removeExistingFirst && !this.childrenByName.isEmpty()) {
- for (ChildNode child : this.childrenByName.values()) {
- // Look for the child in the cache/changed nodes ...
- NodeInfo childInfo = cache.findNodeInfoInCache(child.getUuid());
- if (childInfo != null) childInfo.setParent(null);
- // TODO: These nodes are deleted and should be handled as deletes ...
- }
- this.childrenByUuid.clear();
- this.childrenByName.clear();
- }
- for (Location childLocation : children) {
- UUID childUuid = childLocation.getUuid();
- Path.Segment segment = childLocation.getPath().getLastSegment();
- Name name = segment.getName();
- ChildNode child = new ChildNode(childUuid, name, segment.getIndex());
- this.childrenByName.put(name, child);
- this.childrenByUuid.put(childUuid, child);
- // Look for the child in the cache/changed nodes ...
- NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
- if (childInfo != null) childInfo.setParent(parentUuid);
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- try {
- lock.readLock().lock();
- boolean first = true;
- for (ChildNode child : childrenByName.values()) {
- if (!first) sb.append(", ");
- else first = false;
- sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
- }
- } finally {
- lock.readLock().unlock();
- }
- return sb.toString();
- }
- }
-
- /**
- * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded.
- * Clients should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects
- * and replacing them with new instances.
- */
- @Immutable
- public final static class ChildNode {
- private final UUID uuid;
- private final Name name;
- private final int snsIndex;
-
- protected ChildNode( UUID uuid,
- Name name,
- int snsIndex ) {
- this.uuid = uuid;
- this.name = name;
- this.snsIndex = snsIndex;
- assert this.snsIndex > 0;
- }
-
- /**
- * Get the UUID of the node.
- *
- * @return the node's UUID; never null
- */
- public UUID getUuid() {
- return uuid;
- }
-
- /**
- * Get the name of the node.
- *
- * @return the node's current name; never null
- */
- public Name getName() {
- return name;
- }
-
- /**
- * Get the same-name-sibling index of the node.
- *
- * @return the node's SNS index; always positive
- */
- public int getSnsIndex() {
- return snsIndex;
- }
-
- /**
- * Return a new child node that has a changed SNS index.
- *
- * @param delta the amount the change the SNS index, either positive to increase the value or negative to decrease the
- * value
- * @return the copy of this, with a changed SNS index; never null
- */
- public ChildNode withChangedSnsIndex( int delta ) {
- return new ChildNode(uuid, name, snsIndex + delta);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- return uuid.hashCode();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals( Object obj ) {
- if (obj == this) return true;
- if (obj instanceof ChildNode) {
- return this.uuid.equals(((ChildNode)obj).uuid);
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return uuid.toString() + " ( " + name + "[" + snsIndex + "] )";
- }
- }
-
}
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,133 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ *
+ */
+/**
+ * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded. Clients
+ * should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects and
+ * replacing them with new instances.
+ */
+@Immutable
+public final class ChildNode {
+
+ private final UUID uuid;
+ private final Path.Segment segment;
+
+ public ChildNode( UUID uuid,
+ Path.Segment segment ) {
+ assert uuid != null;
+ assert segment != null;
+ this.uuid = uuid;
+ this.segment = segment;
+ }
+
+ /**
+ * Get the UUID of the node.
+ *
+ * @return the node's UUID; never null
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * Get the path segment for this node.
+ *
+ * @return the path segment; never null
+ */
+ public Path.Segment getSegment() {
+ return segment;
+ }
+
+ /**
+ * Get the name of the node.
+ *
+ * @return the node's current name; never null
+ */
+ public Name getName() {
+ return segment.getName();
+ }
+
+ /**
+ * Get the same-name-sibling index of the node.
+ *
+ * @return the node's SNS index; always positive
+ */
+ public int getSnsIndex() {
+ return segment.getIndex();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return uuid.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildNode) {
+ return this.uuid.equals(((ChildNode)obj).uuid);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return uuid.toString() + " ( " + getSegment() + " )";
+ }
+
+ /**
+ * Obtain a new instance that uses the same {@link #getUuid() UUID} but the supplied path segment.
+ *
+ * @param newSegment the new segment; may not be null
+ * @return the new instance; never null
+ */
+ public ChildNode with( Path.Segment newSegment ) {
+ return new ChildNode(uuid, newSegment);
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ChildNode.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,88 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.Iterator;
+import java.util.UUID;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * Class that maintains the ordered list of {@link ChildNode} instances yet allows fast access to the children with a specified
+ * name.
+ */
+public interface Children extends Iterable<ChildNode> {
+
+ /**
+ * Get the number of children.
+ *
+ * @return the number of children
+ */
+ int size();
+
+ /**
+ * The UUID of the parent node.
+ *
+ * @return the parent node's UUID
+ */
+ UUID getParentUuid();
+
+ /**
+ * Get the child with the given UUID.
+ *
+ * @param uuid the UUID of the child node
+ * @return the child node, or null if there is no child with the supplied UUID
+ */
+ ChildNode getChild( UUID uuid );
+
+ /**
+ * Get the child given the path segment.
+ *
+ * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+ * {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+ * @return the information for the child node, or null if no such child existed
+ */
+ ChildNode getChild( Path.Segment segment );
+
+ /**
+ * Get the same-name-sibling children that all share the supplied name, in order of increasing SNS index.
+ *
+ * @param name the name for the children; may not be null
+ * @return the children with the supplied name; never null
+ */
+ Iterator<ChildNode> getChildren( Name name );
+
+ /**
+ * Create another Children object that is equivalent to this node but with the supplied child added.
+ *
+ * @param newChildName the name of the new child; may not be null
+ * @param newChildUuid the UUID of the new child; may not be null
+ * @param pathFactory the factory that can be used to create Path and/or Path.Segment instances.
+ * @return the new Children object; never null
+ */
+ Children with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory );
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/Children.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,117 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.Iterator;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.EmptyIterator;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * An immutable implementation of {@link Children}.
+ */
+@Immutable
+public final class EmptyChildren implements Children {
+
+ private static final Iterator<ChildNode> EMPTY_ITERATOR = new EmptyIterator<ChildNode>();
+
+ private final UUID parentUuid;
+
+ public EmptyChildren( UUID parentUuid ) {
+ this.parentUuid = parentUuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#size()
+ */
+ public int size() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<ChildNode> iterator() {
+ return EMPTY_ITERATOR;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getParentUuid()
+ */
+ public UUID getParentUuid() {
+ return parentUuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID)
+ */
+ public ChildNode getChild( UUID uuid ) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment)
+ */
+ public ChildNode getChild( Segment segment ) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name)
+ */
+ public Iterator<ChildNode> getChildren( Name name ) {
+ return EMPTY_ITERATOR;
+ }
+
+ public ImmutableChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory ) {
+ return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "";
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/EmptyChildren.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,164 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.ReadOnlyIterator;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+
+/**
+ * An immutable implementation of {@link Children}.
+ */
+@Immutable
+public final class ImmutableChildren implements Children {
+ private final UUID parentUuid;
+ private final Map<UUID, ChildNode> childrenByUuid;
+ private final ListMultimap<Name, ChildNode> childrenByName;
+
+ public ImmutableChildren( UUID parentUuid,
+ Iterable<Location> children ) {
+ this.parentUuid = parentUuid;
+ this.childrenByUuid = new HashMap<UUID, ChildNode>();
+ this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+ for (Location childLocation : children) {
+ UUID childUuid = childLocation.getUuid();
+ Path.Segment segment = childLocation.getPath().getLastSegment();
+ Name name = segment.getName();
+ ChildNode child = new ChildNode(childUuid, segment);
+ this.childrenByName.put(name, child);
+ this.childrenByUuid.put(childUuid, child);
+ }
+ }
+
+ protected ImmutableChildren( Children original,
+ Name additionalChildName,
+ UUID childUuid,
+ PathFactory pathFactory ) {
+ this.parentUuid = original.getParentUuid();
+ this.childrenByUuid = new HashMap<UUID, ChildNode>();
+ this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+ Iterator<ChildNode> iter = original.iterator();
+ while (iter.hasNext()) {
+ ChildNode child = iter.next();
+ this.childrenByName.put(child.getName(), child);
+ this.childrenByUuid.put(child.getUuid(), child);
+ }
+ List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName);
+ Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1);
+ ChildNode additionalChild = new ChildNode(childUuid, segment);
+ this.childrenByName.put(additionalChildName, additionalChild);
+ this.childrenByUuid.put(childUuid, additionalChild);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#size()
+ */
+ public int size() {
+ return childrenByName.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Iterable#iterator()
+ */
+ public Iterator<ChildNode> iterator() {
+ return new ReadOnlyIterator<ChildNode>(this.childrenByName.values().iterator());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getParentUuid()
+ */
+ public UUID getParentUuid() {
+ return parentUuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID)
+ */
+ public ChildNode getChild( UUID uuid ) {
+ return this.childrenByUuid.get(uuid);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment)
+ */
+ public ChildNode getChild( Segment segment ) {
+ List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+ int snsIndex = segment.getIndex();
+ if (childrenWithName.size() < snsIndex) return null;
+ return childrenWithName.get(snsIndex - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name)
+ */
+ public Iterator<ChildNode> getChildren( Name name ) {
+ return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator());
+ }
+
+ public ImmutableChildren with( Name newChildName,
+ UUID newChildUuid,
+ PathFactory pathFactory ) {
+ return new ImmutableChildren(this, newChildName, newChildUuid, pathFactory);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (ChildNode child : childrenByName.values()) {
+ if (!first) sb.append(", ");
+ else first = false;
+ sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
+ }
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableChildren.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,155 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ */
+@Immutable
+public class ImmutableNodeInfo implements NodeInfo {
+ private final Location originalLocation;
+ private final UUID uuid;
+ private final UUID parent;
+ private final Name primaryTypeName;
+ private final NodeDefinitionId definition;
+ private final Children children;
+ private final Map<Name, PropertyInfo> properties;
+
+ /**
+ * Create an immutable NodeInfo instance.
+ *
+ * @param originalLocation the original location
+ * @param primaryTypeName the name of the node's primary type
+ * @param definition the definition used when creating the node
+ * @param parent the parent
+ * @param children the immutable children
+ * @param properties the unmodifiable map of properties
+ */
+ public ImmutableNodeInfo( Location originalLocation,
+ Name primaryTypeName,
+ NodeDefinitionId definition,
+ UUID parent,
+ Children children,
+ Map<Name, PropertyInfo> properties ) {
+ this.originalLocation = originalLocation;
+ this.primaryTypeName = primaryTypeName;
+ this.definition = definition;
+ this.parent = parent;
+ this.uuid = this.originalLocation.getUuid();
+ this.children = children;
+ this.properties = properties;
+ assert this.uuid != null;
+ assert this.definition != null;
+ assert this.primaryTypeName != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getOriginalLocation()
+ */
+ public Location getOriginalLocation() {
+ return originalLocation;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getUuid()
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getParent()
+ */
+ public UUID getParent() {
+ return parent;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPrimaryTypeName()
+ */
+ public Name getPrimaryTypeName() {
+ return primaryTypeName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getDefinitionId()
+ */
+ public NodeDefinitionId getDefinitionId() {
+ return definition;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getChildren()
+ */
+ public Children getChildren() {
+ return children;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#hasProperties()
+ */
+ public boolean hasProperties() {
+ return this.properties.size() != 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getPropertyNames()
+ */
+ public Set<Name> getPropertyNames() {
+ return this.properties.keySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.cache.NodeInfo#getProperty(org.jboss.dna.graph.property.Name)
+ */
+ public PropertyInfo getProperty( Name name ) {
+ return this.properties.get(name);
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/ImmutableNodeInfo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,90 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.Set;
+import java.util.UUID;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.NodeDefinitionId;
+
+/**
+ * The information that describes a node. This is the information that is kept in the cache.
+ */
+public interface NodeInfo {
+
+ /**
+ * @return location
+ */
+ public Location getOriginalLocation();
+
+ /**
+ * @return uuid
+ */
+ public UUID getUuid();
+
+ /**
+ * @return parent
+ */
+ public UUID getParent();
+
+ /**
+ * @return primaryTypeName
+ */
+ public Name getPrimaryTypeName();
+
+ /**
+ * @return definition
+ */
+ public NodeDefinitionId getDefinitionId();
+
+ /**
+ * Get the children for this node.
+ *
+ * @return the children; never null but possibly empty
+ */
+ public Children getChildren();
+
+ /**
+ * Return true of this node has at least one property.
+ *
+ * @return true if there is at least one property, or false if there are none
+ */
+ public boolean hasProperties();
+
+ /**
+ * Get the names of the properties that are owned by this node.
+ *
+ * @return the unmodifiable set of property names
+ */
+ public Set<Name> getPropertyNames();
+
+ /**
+ * Get this node's property that has the supplied name.
+ *
+ * @param name the property name; may not be null
+ * @return the property information, or null if this node has no property with the supplied name
+ */
+ public PropertyInfo getProperty( Name name );
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/NodeInfo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -0,0 +1,168 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr.cache;
+
+import java.util.UUID;
+import javax.jcr.PropertyType;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.PropertyDefinitionId;
+import org.jboss.dna.jcr.PropertyId;
+
+/**
+ * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
+ * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
+ * type}.
+ * <p>
+ * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an instance
+ * by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their reference. This
+ * is because these instances are replaced and discarded whenever anything about the property changes.
+ * </p>
+ */
+@Immutable
+public class PropertyInfo {
+ private final PropertyId propertyId;
+ private final PropertyDefinitionId definitionId;
+ private final Property dnaProperty;
+ private final int propertyType;
+ private final boolean multiValued;
+
+ public PropertyInfo( PropertyId propertyId,
+ PropertyDefinitionId definitionId,
+ int propertyType,
+ Property dnaProperty,
+ boolean multiValued ) {
+ this.propertyId = propertyId;
+ this.definitionId = definitionId;
+ this.propertyType = propertyType;
+ this.dnaProperty = dnaProperty;
+ this.multiValued = multiValued;
+ }
+
+ /**
+ * Get the durable identifier for this property.
+ *
+ * @return propertyId
+ */
+ public PropertyId getPropertyId() {
+ return propertyId;
+ }
+
+ /**
+ * Get the UUID of the node to which this property belongs.
+ *
+ * @return the owner node's UUID; never null
+ */
+ public UUID getNodeUuid() {
+ return propertyId.getNodeId();
+ }
+
+ /**
+ * The identifier for the property definition.
+ *
+ * @return the property definition ID; never null
+ */
+ public PropertyDefinitionId getDefinitionId() {
+ return definitionId;
+ }
+
+ /**
+ * Get the DNA Property, which contains the name and value(s)
+ *
+ * @return the property; never null
+ */
+ public Property getProperty() {
+ return dnaProperty;
+ }
+
+ /**
+ * Get the property name.
+ *
+ * @return the property name; never null
+ */
+ public Name getPropertyName() {
+ return dnaProperty.getName();
+ }
+
+ /**
+ * Get the JCR {@link PropertyType} for this property.
+ *
+ * @return the property type
+ */
+ public int getPropertyType() {
+ return propertyType;
+ }
+
+ /**
+ * @return multiValued
+ */
+ public boolean isMultiValued() {
+ return multiValued;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return propertyId.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertyInfo) {
+ return propertyId.equals(((PropertyInfo)obj).getPropertyId());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(propertyId);
+ sb.append(" defined by ").append(definitionId);
+ sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
+ if (dnaProperty.isSingle()) {
+ sb.append(" with value ");
+ } else {
+ sb.append(" with values ");
+ }
+ sb.append(dnaProperty.getValuesAsArray());
+ return sb.toString();
+ }
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/cache/PropertyInfo.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -33,7 +33,6 @@
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
@@ -53,9 +52,10 @@
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.EmptyChildren;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@@ -129,7 +129,7 @@
node = new MockAbstractJcrNode(cache, uuid);
// Create the children container for the node ...
- children = new Children(uuid);
+ children = new EmptyChildren(uuid);
stub(info.getChildren()).toReturn(children);
}
@@ -149,6 +149,12 @@
return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
}
+ protected void addChild( Name childName,
+ UUID childUuid ) {
+ children = children.with(childName, childUuid, context.getValueFactories().getPathFactory());
+ stub(info.getChildren()).toReturn(children);
+ }
+
@Test
public void shouldAllowVisitation() throws Exception {
ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -285,7 +291,7 @@
public void shouldReturnChildNodeFromGetNodeWithValidName() throws Exception {
AbstractJcrNode child = mock(AbstractJcrNode.class);
UUID childUuid = UUID.randomUUID();
- children.append(cache, name("child"), childUuid);
+ addChild(name("child"), childUuid);
stub(cache.findJcrNode(childUuid)).toReturn(child);
assertThat(node.getNode("child"), is((Node)child));
}
@@ -337,7 +343,7 @@
public void shouldProvideNodeIterator() throws Exception {
AbstractJcrNode child = mock(AbstractJcrNode.class);
UUID childUuid = UUID.randomUUID();
- children.append(cache, name("child"), childUuid);
+ addChild(name("child"), childUuid);
stub(cache.findJcrNode(childUuid)).toReturn(child);
NodeIterator iter = node.getNodes();
assertThat(iter, notNullValue());
@@ -517,10 +523,10 @@
@Test
public void shouldReturnTrueFromHasNodeIfChildWithNameExists() throws Exception {
- children.append(cache, name("child"), UUID.randomUUID());
+ addChild(name("child"), UUID.randomUUID());
assertThat(node.hasNode("child[1]"), is(true));
assertThat(node.hasNode("child[2]"), is(false));
- children.append(cache, name("child"), UUID.randomUUID());
+ addChild(name("child"), UUID.randomUUID());
assertThat(node.hasNode("child[2]"), is(true));
assertThat(node.hasNode("child[3]"), is(false));
}
@@ -537,7 +543,7 @@
@Test
public void shouldReturnTrueFromHasNodesIfThereIsAtLeastOneChild() throws Exception {
- children.append(cache, name("child"), UUID.randomUUID());
+ addChild(name("child"), UUID.randomUUID());
assertThat(node.hasNodes(), is(true));
}
@@ -593,20 +599,14 @@
}
@Test
- @SuppressWarnings( "unchecked" )
public void shouldReturnTrueFromHasPropertiesIfNodeHasAtLeastOneProperty() throws Exception {
- Map<Name, PropertyInfo> properties = mock(Map.class);
- stub(properties.size()).toReturn(5);
- stub(info.getProperties()).toReturn(properties);
+ stub(info.hasProperties()).toReturn(true);
assertThat(node.hasProperties(), is(true));
}
@Test
- @SuppressWarnings( "unchecked" )
public void shouldReturnFalseFromHasPropertiesIfNodeHasNoProperties() throws Exception {
- Map<Name, PropertyInfo> properties = mock(Map.class);
- stub(properties.size()).toReturn(0);
- stub(info.getProperties()).toReturn(properties);
+ stub(info.hasProperties()).toReturn(false);
assertThat(node.hasProperties(), is(false));
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -43,8 +43,8 @@
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -36,7 +36,8 @@
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import org.jboss.dna.graph.property.basic.BasicName;
-import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.graph.property.basic.BasicPathSegment;
+import org.jboss.dna.jcr.cache.ChildNode;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -60,7 +61,7 @@
childNodes = new ArrayList<Node>();
for (int i = 0; i != 10; ++i) {
UUID uuid = UUID.randomUUID();
- ChildNode child = new ChildNode(uuid, new BasicName("", "name"), i + 1);
+ ChildNode child = new ChildNode(uuid, new BasicPathSegment(new BasicName("", "name"), i + 1));
AbstractJcrNode node = mock(AbstractJcrNode.class);
stub(cache.findJcrNode(uuid)).toReturn(node);
children.add(child);
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -34,7 +34,7 @@
import javax.jcr.nodetype.PropertyDefinition;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -32,7 +32,7 @@
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.cache.NodeInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -39,7 +39,7 @@
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-16 22:50:09 UTC (rev 777)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-17 15:43:09 UTC (rev 778)
@@ -49,9 +49,9 @@
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.Property;
-import org.jboss.dna.jcr.SessionCache.Children;
-import org.jboss.dna.jcr.SessionCache.NodeInfo;
-import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.jboss.dna.jcr.cache.Children;
+import org.jboss.dna.jcr.cache.NodeInfo;
+import org.jboss.dna.jcr.cache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -212,7 +212,7 @@
UUID uuid = dnaNode.getLocation().getUuid();
assertThat(nodeInfo.getUuid(), is(uuid));
assertThat(nodeInfo.getOriginalLocation().getUuid(), is(uuid));
- Set<Name> propertyNames = nodeInfo.getProperties().keySet();
+ Set<Name> propertyNames = nodeInfo.getPropertyNames();
for (Name propertyName : propertyNames) {
PropertyInfo info = nodeInfo.getProperty(propertyName);
assertThat(info.getNodeUuid(), is(uuid));
@@ -246,7 +246,7 @@
assertThat(rootInfo.getPrimaryTypeName(), is(name("dna:root")));
assertThat(rootInfo.getOriginalLocation().getPath().isRoot(), is(true));
assertThat(rootInfo.getOriginalLocation().getUuid(), is(rootUuid));
- Set<Name> rootProperties = rootInfo.getProperties().keySet();
+ Set<Name> rootProperties = rootInfo.getPropertyNames();
assertThat(rootProperties, hasItems(name("jcr:primaryType")));
assertSameProperties(rootInfo, root);
15 years, 1 month
DNA SVN: r777 - in trunk/dna-jcr/src: main/resources/org/jboss/dna/jcr and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-16 18:50:09 -0400 (Mon, 16 Mar 2009)
New Revision: 777
Added:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
trunk/dna-jcr/src/test/resources/vehicles.xml
Removed:
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
trunk/dna-jcr/src/test/resources/cars.xml
Log:
DNA-194 Implement update JCR capability
Applied the patch file (just recently attached to the issue) that captures the refactoring to pull the management of the session's cached and updated state out of the individual Node and Property implementations and into a new "SessionCache" class.
This class does a couple of different things. First, it centralizes all of the information about the nodes and properties that have been loaded from the repository source, making it easier to track the cached information as well as track/manage the changes made within the session. Remember that some activities, such as "refresh" or "save" can be performed on one node but could potentially affect many other objects. By centralizing all the cached and transient state, it's much easier to manage all this information. It also makes it much easier to alter the data structure used to manage the cache (for example to make it thread-safe), should that be required at a later time.
Second, it manages the Node and Property implementation objects (which can be held onto by clients), ensuring that the same Node and Property instances are always returned for the same logical instance. These instances are managed by ReferenceMaps, allowing the garbage collector to remove entries when clients no longer hold onto the Node and Property objects (with normal references). The implementation classes of Node and Property (e.g., AbstractJcrNode and its concrete subclasses, and AbstractJcrProperty and its concrete subclasses) no longer directly hold onto the detailed node and property information being cached in SessionCache, but rather hold identifiers and dynamically look up the information when necessary. This ensures that a particular Node implementation object obtained for a node will always present the most up-to-date information about the node, even if that information is changed by the session or other objects.
(Note that it would be very easy for each Node and Property implementation to locally cache the reference to the SessionCache's node information and/or property information, saving some repeated lookups using the SessionCache's methods, but since the SessionCache knows about the implementations it could invalidate these local references when needed. This could be done at a later time when we've proven that the current simple approach needs to be improved.)
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrItem.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -26,26 +26,62 @@
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
/**
* @author jverhaeg
*/
abstract class AbstractJcrItem implements Item {
- protected final String getPath( String absolutePath,
- String relativePath ) {
- assert absolutePath != null;
- assert absolutePath.length() > 0;
- assert relativePath != null;
- if (absolutePath.charAt(absolutePath.length() - 1) == '/') {
- return absolutePath + relativePath;
- }
- return absolutePath + '/' + relativePath;
+ protected final SessionCache cache;
+
+ protected AbstractJcrItem( SessionCache cache ) {
+ assert cache != null;
+ this.cache = cache;
}
/**
* {@inheritDoc}
*
+ * @see javax.jcr.Item#getSession()
+ */
+ public Session getSession() {
+ return cache.session();
+ }
+
+ final JcrSession session() {
+ return cache.session();
+ }
+
+ final ExecutionContext context() {
+ return cache.context();
+ }
+
+ final Name nameFrom( String name ) {
+ return context().getValueFactories().getNameFactory().create(name);
+ }
+
+ final Path pathFrom( String path ) {
+ return context().getValueFactories().getPathFactory().create(path);
+ }
+
+ final Path.Segment segmentFrom( String segment ) {
+ return context().getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ final NamespaceRegistry namespaces() {
+ return context().getNamespaceRegistry();
+ }
+
+ abstract Path path() throws RepositoryException;
+
+ /**
+ * {@inheritDoc}
+ *
* @return <code>false</code>
* @see javax.jcr.Item#isModified()
*/
@@ -96,7 +132,7 @@
*
* @see javax.jcr.Item#getAncestor(int)
*/
- public final Item getAncestor( int depth ) throws RepositoryException {
+ public Item getAncestor( int depth ) throws RepositoryException {
if (depth < 0) {
throw new ItemNotFoundException(JcrI18n.noNegativeDepth.text(depth));
}
@@ -127,7 +163,7 @@
* @see javax.jcr.Item#getDepth()
*/
public int getDepth() throws RepositoryException {
- return getParent().getDepth() + 1;
+ return path().size();
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNode.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -25,11 +25,10 @@
import java.io.InputStream;
import java.util.Calendar;
-import java.util.HashSet;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import javax.jcr.Item;
@@ -42,7 +41,6 @@
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
-import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.lock.Lock;
@@ -51,93 +49,51 @@
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
-import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.i18n.I18n;
import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.ValueFormatException;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
/**
- * @author jverhaeg
+ * An abstract implementation of the JCR {@link Node} interface. Instances of this class are created and managed by the
+ * {@link SessionCache}. Each instance references the {@link SessionCache.NodeInfo node information} also managed by the
+ * SessionCache, and finds and operates against this information with each method call.
*/
-@NotThreadSafe
+@Immutable
abstract class AbstractJcrNode extends AbstractJcrItem implements Node {
private static final NodeType[] EMPTY_NODE_TYPES = new NodeType[] {};
- private final JcrSession session;
- private final NodeDefinition definition;
- protected Location location;
- private Map<Name, Property> properties;
- private List<Location> children;
+ protected final UUID nodeUuid;
- AbstractJcrNode( JcrSession session,
- Location location,
- NodeDefinition definition ) {
- assert session != null;
- assert definition != null;
- assert location != null;
- this.location = location;
- this.session = session;
- this.definition = definition;
+ AbstractJcrNode( SessionCache cache,
+ UUID nodeUuid ) {
+ super(cache);
+ this.nodeUuid = nodeUuid;
}
- /**
- * {@inheritDoc}
- *
- * @see javax.jcr.Item#getSession()
- */
- public Session getSession() {
- return session;
- }
+ abstract boolean isRoot();
- final JcrSession session() {
- return session;
- }
-
final UUID internalUuid() {
- return location.getUuid();
+ return nodeUuid;
}
- /**
- * Change the internal UUID. Technically, it is possible for this to change, but only because a new node may be assigned a
- * UUID when it is created within the session and (according to the spec) this UUID can change when the new node is persisted
- * upon {@link #save()}.
- *
- * @param uuid the new UUID; may not be null
- */
- final void setInternalUuid( UUID uuid ) {
- assert uuid != null;
- location = location.with(uuid);
+ final NodeInfo nodeInfo() throws ItemNotFoundException, RepositoryException {
+ return cache.findNodeInfo(nodeUuid);
}
- final void setChildren( List<Location> children ) {
- assert children != null;
- this.children = children;
+ @Override
+ Path path() throws RepositoryException {
+ return cache.getPathFor(nodeInfo());
}
- final void setProperties( Map<Name, Property> properties ) {
- assert properties != null;
- this.properties = properties;
- }
-
/**
- * Utility mehtod to create a {@link Name} from the supplied string.
- *
- * @param name the string
- * @return the name, or null if the supplied string is null
- */
- final Name nameFrom( String name ) {
- return session.getExecutionContext().getValueFactories().getNameFactory().create(name);
- }
-
- final NamespaceRegistry namespaces() {
- return session.getExecutionContext().getNamespaceRegistry();
- }
-
- /**
* {@inheritDoc}
*
* @see javax.jcr.Node#getUUID()
@@ -148,7 +104,7 @@
if (mixinsProp != null) {
String referenceableMixinName = JcrMixLexicon.REFERENCEABLE.getString(namespaces());
for (Value value : mixinsProp.getValues()) {
- if (referenceableMixinName.equals(value.getString())) return getProperty(JcrLexicon.UUID).getString();
+ if (referenceableMixinName.equals(value.getString())) return nodeUuid.toString();
}
}
throw new UnsupportedRepositoryOperationException();
@@ -192,8 +148,9 @@
*
* @see javax.jcr.Node#getDefinition()
*/
- public NodeDefinition getDefinition() {
- return definition;
+ public NodeDefinition getDefinition() throws RepositoryException {
+ NodeDefinitionId definitionId = nodeInfo().getDefinitionId();
+ return session().nodeTypeManager().getNodeDefinition(definitionId);
}
/**
@@ -203,8 +160,8 @@
* @see javax.jcr.Node#getPrimaryNodeType()
*/
public NodeType getPrimaryNodeType() throws RepositoryException {
- String nodeTypeName = getProperty(JcrLexicon.PRIMARY_TYPE).getString();
- return session.getWorkspace().getNodeTypeManager().getNodeType(nodeTypeName);
+ Name primaryTypeName = nodeInfo().getPrimaryTypeName();
+ return session().nodeTypeManager().getNodeType(primaryTypeName);
}
/**
@@ -214,7 +171,7 @@
* @see javax.jcr.Node#getMixinNodeTypes()
*/
public NodeType[] getMixinNodeTypes() throws RepositoryException {
- NodeTypeManager nodeTypeManager = session.getWorkspace().getNodeTypeManager();
+ NodeTypeManager nodeTypeManager = session().getWorkspace().getNodeTypeManager();
Property mixinTypesProperty = getProperty(JcrLexicon.MIXIN_TYPES);
if (mixinTypesProperty == null) return EMPTY_NODE_TYPES;
List<NodeType> mixinNodeTypes = new LinkedList<NodeType>();
@@ -252,12 +209,35 @@
* @see javax.jcr.Node#getPrimaryItem()
*/
public final Item getPrimaryItem() throws RepositoryException {
- // TODO: Check if declared in the node type first
+ // Get the primary item name from this node's type ...
+ NodeType primaryType = getPrimaryNodeType();
+ String primaryItemNameString = primaryType.getPrimaryItemName();
+ if (primaryItemNameString == null) {
+ I18n msg = JcrI18n.noPrimaryItemNameDefinedOnPrimaryType;
+ throw new ItemNotFoundException(msg.text(primaryType.getName(), getPath(), cache.workspaceName()));
+ }
try {
- Property primaryItemProp = getProperty("jcr:primaryItemName");
- return session.getItem(getPath(getPath(), primaryItemProp.getString()));
+ Path primaryItemPath = context().getValueFactories().getPathFactory().create(primaryItemNameString);
+ if (primaryItemPath.size() != 1 || primaryItemPath.isAbsolute()) {
+ I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
+ throw new ItemNotFoundException(msg.text(primaryType.getName(),
+ primaryItemNameString,
+ getPath(),
+ cache.workspaceName()));
+ }
+ return cache.findJcrItem(nodeUuid, primaryItemPath);
+ } catch (ValueFormatException error) {
+ I18n msg = JcrI18n.primaryItemNameForPrimaryTypeIsNotValid;
+ throw new ItemNotFoundException(msg.text(primaryType.getName(),
+ primaryItemNameString,
+ getPath(),
+ cache.workspaceName()));
} catch (PathNotFoundException error) {
- throw new ItemNotFoundException(error);
+ I18n msg = JcrI18n.primaryItemDoesNotExist;
+ throw new ItemNotFoundException(msg.text(primaryType.getName(),
+ primaryItemNameString,
+ getPath(),
+ cache.workspaceName()));
}
}
@@ -268,7 +248,7 @@
* @see javax.jcr.Item#isSame(javax.jcr.Item)
*/
@Override
- public final boolean isSame( Item otherItem ) throws RepositoryException {
+ public boolean isSame( Item otherItem ) throws RepositoryException {
CheckArg.isNotNull(otherItem, "otherItem");
if (super.isSame(otherItem) && otherItem instanceof Node) {
if (otherItem instanceof AbstractJcrNode) {
@@ -285,9 +265,8 @@
*
* @see javax.jcr.Node#hasProperties()
*/
- public final boolean hasProperties() {
- assert properties != null;
- return !properties.isEmpty();
+ public final boolean hasProperties() throws RepositoryException {
+ return nodeInfo().getProperties().size() > 0;
}
/**
@@ -299,10 +278,17 @@
public final boolean hasProperty( String relativePath ) throws RepositoryException {
CheckArg.isNotEmpty(relativePath, "relativePath");
if (relativePath.indexOf('/') >= 0) {
- return (getProperty(relativePath) != null);
+ try {
+ getProperty(relativePath);
+ return true;
+ } catch (PathNotFoundException e) {
+ return false;
+ }
}
- assert properties != null;
- return properties.containsKey(nameFrom(relativePath));
+ if (relativePath.equals(".")) return false;
+ if (relativePath.equals("..")) return false;
+ // Otherwise it should be a property on this node ...
+ return cache.findPropertyInfo(new PropertyId(nodeUuid, nameFrom(relativePath))) != null;
}
/**
@@ -310,8 +296,8 @@
*
* @see javax.jcr.Node#getProperties()
*/
- public final PropertyIterator getProperties() {
- return new JcrPropertyIterator(properties.values());
+ public final PropertyIterator getProperties() throws RepositoryException {
+ return new JcrPropertyIterator(cache.findJcrPropertiesFor(nodeUuid));
}
/**
@@ -323,40 +309,67 @@
CheckArg.isNotNull(namePattern, "namePattern");
namePattern = namePattern.trim();
if (namePattern.length() == 0) return new JcrEmptyPropertyIterator();
- if ("*".equals(namePattern)) return new JcrPropertyIterator(properties.values());
+ Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeUuid);
+ if ("*".equals(namePattern)) return new JcrPropertyIterator(properties);
+
+ // Figure out the patterns for each of the different disjunctions in the supplied pattern ...
List<Object> patterns = createPatternsFor(namePattern);
- // Implementing exact-matching only for now to prototype types as properties
- Set<Property> matchingProps = new HashSet<Property>();
- boolean foundMatch = false;
- for (Property property : properties.values()) {
+ // Go through the properties and remove any property that doesn't match a pattern ...
+ boolean foundMatch = true;
+ Collection<AbstractJcrProperty> matchingProperties = new LinkedList<AbstractJcrProperty>();
+ Iterator<AbstractJcrProperty> iter = properties.iterator();
+ while (iter.hasNext()) {
+ AbstractJcrProperty property = iter.next();
String propName = property.getName();
+ assert foundMatch == true;
for (Object patternOrMatch : patterns) {
if (patternOrMatch instanceof Pattern) {
Pattern pattern = (Pattern)patternOrMatch;
- if (pattern.matcher(propName).matches()) foundMatch = true;
+ if (pattern.matcher(propName).matches()) break;
} else {
String match = (String)patternOrMatch;
- if (propName.equals(match)) foundMatch = true;
+ if (propName.equals(match)) break;
}
- if (foundMatch) {
- foundMatch = false;
- matchingProps.add(property);
- break;
- }
+ // No pattern matched ...
+ foundMatch = false;
}
+ if (foundMatch) {
+ matchingProperties.add(property);
+ foundMatch = true; // for the next iteration ..
+ }
}
- return new JcrPropertyIterator(matchingProps);
+ return new JcrPropertyIterator(matchingProperties);
}
/**
+ * {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException always
+ * @see javax.jcr.Node#getReferences()
+ */
+ public final PropertyIterator getReferences() throws RepositoryException {
+ // Iterate through the properties to see which ones have a REFERENCE type ...
+ Collection<AbstractJcrProperty> properties = cache.findJcrPropertiesFor(nodeUuid);
+ Collection<AbstractJcrProperty> references = new LinkedList<AbstractJcrProperty>();
+ Iterator<AbstractJcrProperty> iter = properties.iterator();
+ while (iter.hasNext()) {
+ AbstractJcrProperty property = iter.next();
+ if (property.getType() == PropertyType.REFERENCE) references.add(property);
+ }
+ if (references.isEmpty()) return new JcrEmptyPropertyIterator();
+ return new JcrPropertyIterator(references);
+ }
+
+ /**
* A non-standard method to obtain a property given the {@link Name DNA Name} object. This method is faster
*
* @param propertyName the property name
* @return the JCR property with the supplied name, or null if the property doesn't exist
+ * @throws RepositoryException if there is an error finding the property with the supplied name
*/
- public final Property getProperty( Name propertyName ) {
- return properties.get(propertyName);
+ public final Property getProperty( Name propertyName ) throws RepositoryException {
+ return cache.findJcrProperty(new PropertyId(nodeUuid, propertyName));
}
/**
@@ -367,19 +380,23 @@
*/
public final Property getProperty( String relativePath ) throws RepositoryException {
CheckArg.isNotEmpty(relativePath, "relativePath");
- if (relativePath.indexOf('/') >= 0) {
- Item item = session.getItem(getPath(getPath(), relativePath));
+ int indexOfFirstSlash = relativePath.indexOf('/');
+ if (indexOfFirstSlash == 0) {
+ // Not a relative path ...
+ throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
+ }
+ if (indexOfFirstSlash != -1) {
+ // We know it's a relative path with more than one segment ...
+ Path path = pathFrom(relativePath).getNormalizedPath();
+ AbstractJcrItem item = cache.findJcrItem(nodeUuid, path);
if (item instanceof Property) {
return (Property)item;
}
- // The item must be a node.
- assert item instanceof Node;
- // Since session.getItem() gives precedence to nodes over properties, try explicitly looking for the property with the
- // same name as the found node using the returned node's parent.
- return ((Node)item).getParent().getProperty(item.getName());
+ I18n msg = JcrI18n.propertyNotFoundAtPathRelativeToReferenceNode;
+ throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
}
- assert properties != null;
- Property property = properties.get(nameFrom(relativePath));
+ // It's just a name, so look for it directly ...
+ Property property = getProperty(nameFrom(relativePath));
if (property != null) return property;
throw new PathNotFoundException();
}
@@ -387,45 +404,31 @@
/**
* {@inheritDoc}
*
- * @throws UnsupportedOperationException always
- * @see javax.jcr.Node#getReferences()
- */
- public final PropertyIterator getReferences() throws RepositoryException {
- // Iterate through the properties to see which ones have a REFERENCE type ...
- Set<Property> references = new HashSet<Property>();
- for (Property property : properties.values()) {
- if (property.getType() == PropertyType.REFERENCE) references.add(property);
- }
- if (references.isEmpty()) return new JcrEmptyPropertyIterator();
- return new JcrPropertyIterator(references);
- }
-
- /**
- * {@inheritDoc}
- *
* @throws IllegalArgumentException if <code>relativePath</code> is empty or <code>null</code>.
* @see javax.jcr.Node#hasNode(java.lang.String)
*/
public final boolean hasNode( String relativePath ) throws RepositoryException {
CheckArg.isNotEmpty(relativePath, "relativePath");
- if (relativePath.indexOf('/') >= 0) {
- return (getNode(relativePath) != null);
+ if (relativePath.equals(".")) return true;
+ if (relativePath.equals("..")) return isRoot() ? false : true;
+ int indexOfFirstSlash = relativePath.indexOf('/');
+ if (indexOfFirstSlash == 0) {
+ // Not a relative path ...
+ throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
}
- if (children != null) {
+ if (indexOfFirstSlash != -1) {
+ Path path = pathFrom(relativePath).getNormalizedPath();
try {
- Path.Segment segment = session.getExecutionContext()
- .getValueFactories()
- .getPathFactory()
- .createSegment(relativePath);
- for (Location child : children) {
- Path.Segment childSegment = child.getPath().getLastSegment();
- if (childSegment.equals(segment)) return true;
- }
- } catch (ValueFormatException e) {
- throw new RepositoryException(JcrI18n.invalidRelativePath.text(relativePath));
+ AbstractJcrNode item = cache.findJcrNode(nodeUuid, path);
+ return item != null;
+ } catch (PathNotFoundException e) {
+ return false;
}
}
- return false;
+ // It's just a name, so look for a child ...
+ Path.Segment segment = segmentFrom(relativePath);
+ ChildNode child = nodeInfo().getChildren().getChild(segment);
+ return child != null;
}
/**
@@ -433,8 +436,8 @@
*
* @see javax.jcr.Node#hasNodes()
*/
- public final boolean hasNodes() {
- return (children != null && !children.isEmpty());
+ public final boolean hasNodes() throws RepositoryException {
+ return nodeInfo().getChildren().size() > 0;
}
/**
@@ -445,12 +448,31 @@
*/
public final Node getNode( String relativePath ) throws RepositoryException {
CheckArg.isNotEmpty(relativePath, "relativePath");
- String path = getPath(getPath(), relativePath);
- Item item = getSession().getItem(path);
- if (item instanceof Node) {
- return (Node)item;
+ if (relativePath.equals(".")) return this;
+ if (relativePath.equals("..")) return this.getParent();
+ int indexOfFirstSlash = relativePath.indexOf('/');
+ if (indexOfFirstSlash == 0) {
+ // Not a relative path ...
+ throw new IllegalArgumentException(JcrI18n.invalidPathParameter.text(relativePath));
}
- throw new PathNotFoundException();
+ if (indexOfFirstSlash != -1) {
+ // We know it's a relative path with more than one segment ...
+ Path path = pathFrom(relativePath).getNormalizedPath();
+ AbstractJcrItem item = cache.findJcrItem(nodeUuid, path);
+ if (item instanceof Node) {
+ return (Node)item;
+ }
+ I18n msg = JcrI18n.nodeNotFoundAtPathRelativeToReferenceNode;
+ throw new PathNotFoundException(msg.text(relativePath, getPath(), cache.workspaceName()));
+ }
+ // It's just a name, so look for a child ...
+ Path.Segment segment = segmentFrom(relativePath);
+ ChildNode child = nodeInfo().getChildren().getChild(segment);
+ if (child != null) {
+ return cache.findJcrNode(child.getUuid());
+ }
+ String msg = JcrI18n.childNotFoundUnderNode.text(segment, getPath(), cache.workspaceName());
+ throw new PathNotFoundException(msg);
}
/**
@@ -458,11 +480,12 @@
*
* @see javax.jcr.Node#getNodes()
*/
- public final NodeIterator getNodes() {
- if (children == null) {
+ public final NodeIterator getNodes() throws RepositoryException {
+ Children children = nodeInfo().getChildren();
+ if (children.size() == 0) {
return new JcrEmptyNodeIterator();
}
- return new JcrChildNodeIterator(this, children);
+ return new JcrChildNodeIterator(cache, children, children.size());
}
/**
@@ -479,11 +502,12 @@
List<Object> patterns = createPatternsFor(namePattern);
// Implementing exact-matching only for now to prototype types as properties
- List<Location> matchingChildren = new LinkedList<Location>();
+ Children children = nodeInfo().getChildren();
+ List<ChildNode> matchingChildren = new LinkedList<ChildNode>();
NamespaceRegistry registry = namespaces();
boolean foundMatch = false;
- for (Location child : children) {
- String childName = child.getPath().getLastSegment().getName().getString(registry);
+ for (ChildNode child : children) {
+ String childName = child.getName().getString(registry);
for (Object patternOrMatch : patterns) {
if (patternOrMatch instanceof Pattern) {
Pattern pattern = (Pattern)patternOrMatch;
@@ -499,7 +523,7 @@
}
}
}
- return new JcrChildNodeIterator(this, matchingChildren);
+ return new JcrChildNodeIterator(cache, matchingChildren, matchingChildren.size());
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrNodeTypeSource.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -44,9 +44,9 @@
protected static final List<JcrNodeDefinition> NO_CHILD_NODES = Collections.<JcrNodeDefinition>emptyList();
protected static final List<JcrPropertyDefinition> NO_PROPERTIES = Collections.<JcrPropertyDefinition>emptyList();
- // Indicates that the node type has no primary item name - added for readability
+ /** Indicates that the node type has no primary item name - added for readability */
protected static final Name NO_PRIMARY_ITEM_NAME = null;
- // Indicates that the definition should apply to all property definition or child node definitions - added for readability
+ /** Indicates that the definition should apply to all property definition or child node definitions - added for readability */
protected static final Name ALL_NODES = null;
// Indicates whether or not the node type is a mixin - added for readability
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/AbstractJcrProperty.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -28,14 +28,16 @@
import javax.jcr.Item;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
-import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
/**
* @author jverhaeg
@@ -43,22 +45,13 @@
@NotThreadSafe
abstract class AbstractJcrProperty extends AbstractJcrItem implements Property {
- private final AbstractJcrNode node;
- private final org.jboss.dna.graph.property.Property dnaProperty;
- private final PropertyDefinition jcrPropertyDefinition;
- private final int propertyType;
+ private final PropertyId propertyId;
- AbstractJcrProperty( AbstractJcrNode node,
- PropertyDefinition definition,
- int propertyType,
- org.jboss.dna.graph.property.Property dnaProperty ) {
- assert node != null;
- assert dnaProperty != null;
- assert definition != null;
- this.node = node;
- this.dnaProperty = dnaProperty;
- this.jcrPropertyDefinition = definition;
- this.propertyType = propertyType;
+ AbstractJcrProperty( SessionCache cache,
+ PropertyId propertyId ) {
+ super(cache);
+ assert propertyId != null;
+ this.propertyId = propertyId;
}
/**
@@ -72,25 +65,34 @@
visitor.visit(this);
}
- JcrValue createValue( Object value ) {
- return new JcrValue(getExecutionContext().getValueFactories(), getType(), value);
+ final PropertyInfo propertyInfo() throws PathNotFoundException, RepositoryException {
+ return cache.findPropertyInfo(propertyId);
}
- final ExecutionContext getExecutionContext() {
- return node.session().getExecutionContext();
+ final Name name() throws RepositoryException {
+ return propertyInfo().getPropertyName();
}
- final org.jboss.dna.graph.property.Property getDnaProperty() {
- return dnaProperty;
+ final org.jboss.dna.graph.property.Property property() throws RepositoryException {
+ return propertyInfo().getProperty();
}
+ JcrValue createValue( Object value ) throws RepositoryException {
+ return new JcrValue(context().getValueFactories(), propertyInfo().getPropertyType(), value);
+ }
+
+ @Override
+ Path path() throws RepositoryException {
+ return cache.getPathFor(propertyInfo());
+ }
+
/**
* {@inheritDoc}
*
* @see javax.jcr.Property#getType()
*/
- public final int getType() {
- return propertyType;
+ public int getType() throws RepositoryException {
+ return propertyInfo().getPropertyType();
}
/**
@@ -98,8 +100,11 @@
*
* @see javax.jcr.Property#getDefinition()
*/
- public final PropertyDefinition getDefinition() {
- return jcrPropertyDefinition;
+ public final PropertyDefinition getDefinition() throws RepositoryException {
+ PropertyInfo info = propertyInfo();
+ PropertyDefinitionId definitionId = info.getDefinitionId();
+ boolean multiValued = info.isMultiValued();
+ return cache.session().nodeTypeManager().getPropertyDefinition(definitionId, multiValued);
}
/**
@@ -111,8 +116,8 @@
*
* @see javax.jcr.Item#getName()
*/
- public final String getName() {
- return dnaProperty.getName().getString(node.namespaces());
+ public final String getName() throws RepositoryException {
+ return propertyInfo().getPropertyName().getString(namespaces());
}
/**
@@ -120,8 +125,8 @@
*
* @see javax.jcr.Item#getParent()
*/
- public final Node getParent() {
- return node;
+ public final Node getParent() throws RepositoryException {
+ return cache.findJcrNode(propertyId.getNodeId());
}
/**
@@ -130,21 +135,12 @@
* @see javax.jcr.Item#getPath()
*/
public final String getPath() throws RepositoryException {
- return getPath(node.getPath(), getName());
+ return cache.getPathFor(propertyInfo()).getString(namespaces());
}
/**
* {@inheritDoc}
*
- * @see javax.jcr.Item#getSession()
- */
- public final Session getSession() {
- return node.getSession();
- }
-
- /**
- * {@inheritDoc}
- *
* @return false
* @see javax.jcr.Item#isNode()
*/
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/DnaBuiltinNodeTypeSource.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -127,18 +127,25 @@
NO_PRIMARY_ITEM_NAME, Arrays.asList(new JcrNodeDefinition[] {
new JcrNodeDefinition(session, null, JcrLexicon.SYSTEM,
OnParentVersionBehavior.IGNORE.getJcrValue(), true, true,
- true, false, DnaLexicon.NAMESPACES,
- new NodeType[] {namespaces}),
- new JcrNodeDefinition(session, null, null,
+ true, false, DnaLexicon.SYSTEM, new NodeType[] {system}),
+ new JcrNodeDefinition(session, null, ALL_NODES,
OnParentVersionBehavior.VERSION.getJcrValue(), false, false,
- false, true, DnaLexicon.NAMESPACES,
- new NodeType[] {namespaces}),
+ false, true, JcrNtLexicon.UNSTRUCTURED,
+ new NodeType[] {base}),
- }), NO_PROPERTIES, NOT_MIXIN, UNORDERABLE_CHILD_NODES);
+ }), Arrays.asList(new JcrPropertyDefinition[] {
+ new JcrPropertyDefinition(session, null, ALL_NODES,
+ OnParentVersionBehavior.COPY.getJcrValue(), false,
+ false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
+ NO_CONSTRAINTS, false),
+ new JcrPropertyDefinition(session, null, ALL_NODES,
+ OnParentVersionBehavior.COPY.getJcrValue(), false,
+ false, false, NO_DEFAULT_VALUES, PropertyType.UNDEFINED,
+ NO_CONSTRAINTS, true),}), NOT_MIXIN,
+ ORDERABLE_CHILD_NODES);
-
- primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace, }));
- mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] { }));
+ primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {root, system, namespaces, namespace,}));
+ mixinNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {}));
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrChildNodeIterator.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -24,34 +24,31 @@
package org.jboss.dna.jcr;
import java.util.Iterator;
-import java.util.List;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import net.jcip.annotations.Immutable;
import org.jboss.dna.common.util.CheckArg;
-import org.jboss.dna.graph.Location;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
/**
- * @author jverhaeg
*/
@Immutable
final class JcrChildNodeIterator implements NodeIterator {
- private final AbstractJcrNode parent;
- private final Iterator<Location> iterator;
- private final JcrSession session;
+ private final SessionCache cache;
+ private final Iterator<ChildNode> iterator;
private int ndx;
private int size;
- JcrChildNodeIterator( AbstractJcrNode parent,
- List<Location> children ) {
- assert parent != null;
+ JcrChildNodeIterator( SessionCache cache,
+ Iterable<ChildNode> children,
+ int size ) {
+ assert cache != null;
assert children != null;
- this.parent = parent;
- this.session = parent.session();
+ this.cache = cache;
iterator = children.iterator();
- size = children.size();
+ this.size = size;
}
/**
@@ -96,10 +93,10 @@
* @see javax.jcr.NodeIterator#nextNode()
*/
public Node nextNode() {
- Location location = iterator.next();
+ ChildNode child = iterator.next();
ndx++;
try {
- return session.getChild(parent, location);
+ return cache.findJcrNode(child.getUuid());
} catch (RepositoryException error) {
throw new RuntimeException(error);
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -57,7 +57,7 @@
class JcrDocumentViewExporter extends AbstractJcrExporter {
private static final TextEncoder VALUE_ENCODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
-
+
JcrDocumentViewExporter( JcrSession session ) {
super(session, Collections.<String>emptyList());
}
@@ -103,7 +103,7 @@
continue;
}
- Name propName = ((AbstractJcrProperty)prop).getDnaProperty().getName();
+ Name propName = ((AbstractJcrProperty)prop).name();
String localPropName = getPrefixedName(propName);
@@ -141,16 +141,15 @@
}
endElement(contentHandler, name);
-
+
}
/**
- * Indicates whether the current node is an XML text node as per section 6.4.2.3 of the JCR 1.0 specification.
- * XML text nodes are nodes that have the name "jcr:xmltext" and only one property (besides the mandatory
- * "jcr:primaryType"). The property must have a property name of "jcr:xmlcharacters", a type of <code>String</code>,
- * and does not have multiple values.<p/>
- * In practice, this is handled in DNA by making XML text nodes have a type of "dna:xmltext", which
- * enforces these property characteristics.
+ * Indicates whether the current node is an XML text node as per section 6.4.2.3 of the JCR 1.0 specification. XML text nodes
+ * are nodes that have the name "jcr:xmltext" and only one property (besides the mandatory
+ * "jcr:primaryType"). The property must have a property name of "jcr:xmlcharacters", a type of
+ * <code>String</code>, and does not have multiple values.<p/> In practice, this is handled in DNA by making XML text nodes
+ * have a type of "dna:xmltext", which enforces these property characteristics.
*
* @param node the node to test
* @return whether this node is a special xml text node
@@ -190,8 +189,8 @@
}
/**
- * Returns the XML characters for the given node.
- * The node must be an XML text node, as defined in {@link #isXmlTextNode(Node)}.
+ * Returns the XML characters for the given node. The node must be an XML text node, as defined in
+ * {@link #isXmlTextNode(Node)}.
*
* @param node the node for which XML characters will be retrieved.
* @return the xml characters for this node
@@ -201,7 +200,7 @@
// ./xmltext/xmlcharacters exception (see JSR-170 Spec 6.4.2.3)
assert isXmlTextNode(node);
-
+
Property xmlCharacters = node.getProperty(getPrefixedName(JcrLexicon.XMLCHARACTERS));
assert xmlCharacters != null;
@@ -221,22 +220,21 @@
/**
* Special {@link TextEncoder} that implements the subset of XML name encoding suggested by section 6.4.4 of the JCR 1.0.1
- * specification. This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
+ * specification. This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
* underscore characters that might otherwise suggest an encoding, as defined in {@link XmlNameEncoder}.
- *
*/
protected static class JcrDocumentViewPropertyEncoder extends XmlNameEncoder {
private static final Set<Character> MAPPED_CHARACTERS;
-
+
static {
MAPPED_CHARACTERS = new HashSet<Character>();
MAPPED_CHARACTERS.add(' ');
MAPPED_CHARACTERS.add('\r');
MAPPED_CHARACTERS.add('\n');
MAPPED_CHARACTERS.add('\t');
-
+
}
-
+
/**
* {@inheritDoc}
*
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrI18n.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -39,6 +39,7 @@
public static I18n inputStreamConsumed;
public static I18n nonInputStreamConsumed;
public static I18n pathNotFound;
+ public static I18n pathNotFoundRelativeTo;
public static I18n permissionDenied;
public static I18n repositoryMustBeConfigured;
public static I18n sourceInUse;
@@ -61,9 +62,19 @@
public static I18n invalidRelativePath;
public static I18n invalidPathParameter;
public static I18n invalidNamePattern;
+ public static I18n noPrimaryItemNameDefinedOnPrimaryType;
+ public static I18n primaryItemNameForPrimaryTypeIsNotValid;
+ public static I18n primaryItemDoesNotExist;
public static I18n itemNotFoundWithUuid;
+ public static I18n itemNotFoundAtPath;
+ public static I18n itemNotFoundAtPathRelativeToReferenceNode;
+ public static I18n propertyNotFoundAtPathRelativeToReferenceNode;
+ public static I18n nodeNotFoundAtPathRelativeToReferenceNode;
+ public static I18n childNotFoundUnderNode;
public static I18n errorWhileFindingNodeWithUuid;
+ public static I18n errorWhileFindingNodeWithPath;
public static I18n nodeDefinitionCouldNotBeDeterminedForNode;
+ public static I18n missingNodeTypeForExistingNode;
public static I18n typeNotFound;
public static I18n supertypeNotFound;
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrItemDefinition.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -37,7 +37,7 @@
protected final JcrSession session;
- private final NodeType declaringNodeType;
+ protected final JcrNodeType declaringNodeType;
protected final Name name;
private final int onParentVersion;
private final boolean autoCreated;
@@ -45,7 +45,7 @@
private final boolean protectedItem;
JcrItemDefinition( JcrSession session,
- NodeType declaringNodeType,
+ JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
boolean autoCreated,
@@ -54,13 +54,20 @@
super();
this.session = session;
this.declaringNodeType = declaringNodeType;
- this.name = name;
+ this.name = name != null ? name : session.getExecutionContext()
+ .getValueFactories()
+ .getNameFactory()
+ .create(JcrNodeType.RESIDUAL_ITEM_NAME);
this.onParentVersion = onParentVersion;
this.autoCreated = autoCreated;
this.mandatory = mandatory;
this.protectedItem = protectedItem;
}
+ final Name getInternalName() {
+ return name;
+ }
+
/**
* {@inheritDoc}
*
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrMultiValueProperty.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -30,7 +30,6 @@
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.graph.property.Property;
@@ -40,11 +39,9 @@
@NotThreadSafe
final class JcrMultiValueProperty extends AbstractJcrProperty {
- JcrMultiValueProperty( AbstractJcrNode node,
- PropertyDefinition definition,
- int propertyType,
- Property dnaProperty ) {
- super(node, definition, propertyType, dnaProperty);
+ JcrMultiValueProperty( SessionCache cache,
+ PropertyId propertyId ) {
+ super(cache, propertyId);
}
/**
@@ -102,7 +99,7 @@
* @see javax.jcr.Property#getLengths()
*/
public long[] getLengths() throws RepositoryException {
- Property dnaProperty = getDnaProperty();
+ Property dnaProperty = propertyInfo().getProperty();
long[] lengths = new long[dnaProperty.size()];
Iterator<?> iter = dnaProperty.iterator();
for (int ndx = 0; iter.hasNext(); ndx++) {
@@ -161,8 +158,8 @@
*
* @see javax.jcr.Property#getValues()
*/
- public Value[] getValues() {
- Property dnaProperty = getDnaProperty();
+ public Value[] getValues() throws RepositoryException {
+ Property dnaProperty = propertyInfo().getProperty();
Value[] values = new JcrValue[dnaProperty.size()];
Iterator<?> iter = dnaProperty.iterator();
for (int ndx = 0; iter.hasNext(); ndx++) {
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNode.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -27,10 +27,7 @@
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeDefinition;
import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Path;
/**
* @author jverhaeg
@@ -38,19 +35,19 @@
@NotThreadSafe
final class JcrNode extends AbstractJcrNode {
- private final UUID parentUuid;
-
- JcrNode( JcrSession session,
- UUID parentUuid,
- Location location,
- NodeDefinition nodeDefinition ) {
- super(session, location, nodeDefinition);
- assert parentUuid != null;
- this.parentUuid = parentUuid;
+ JcrNode( SessionCache cache,
+ UUID nodeUuid ) {
+ super(cache, nodeUuid);
}
- final Path.Segment segment() {
- return location.getPath().getLastSegment();
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNode#isRoot()
+ */
+ @Override
+ boolean isRoot() {
+ return false;
}
/**
@@ -58,8 +55,8 @@
*
* @see javax.jcr.Node#getIndex()
*/
- public int getIndex() {
- return segment().getIndex();
+ public int getIndex() throws RepositoryException {
+ return cache.getSnsIndexOf(nodeUuid);
}
/**
@@ -67,8 +64,8 @@
*
* @see javax.jcr.Item#getName()
*/
- public String getName() {
- return segment().getName().getString(((JcrSession)getSession()).getExecutionContext().getNamespaceRegistry());
+ public String getName() throws RepositoryException {
+ return cache.getNameOf(nodeUuid).getString(namespaces());
}
/**
@@ -76,12 +73,8 @@
*
* @see javax.jcr.Item#getParent()
*/
- public Node getParent() throws ItemNotFoundException {
- Node node = session().getNode(parentUuid);
- if (node == null) {
- throw new ItemNotFoundException();
- }
- return node;
+ public Node getParent() throws ItemNotFoundException, RepositoryException {
+ return cache.findJcrNode(nodeInfo().getParent());
}
/**
@@ -90,20 +83,6 @@
* @see javax.jcr.Item#getPath()
*/
public String getPath() throws RepositoryException {
- Node parent = getParent();
- StringBuilder builder = new StringBuilder(parent.getPath());
- assert builder.length() > 0;
- if (builder.charAt(builder.length() - 1) != '/') {
- builder.append('/');
- }
- String name = getName();
- builder.append(name);
- int ndx = getIndex();
- if (ndx > 1) {
- builder.append('[');
- builder.append(ndx);
- builder.append(']');
- }
- return builder.toString();
+ return cache.getPathFor(nodeUuid).getString(namespaces());
}
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeDefinition.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -47,8 +47,11 @@
/** @see NodeDefinition#getRequiredPrimaryTypes() */
private final NodeType[] requiredPrimaryTypes;
+ /** A durable identifier for this node definition. */
+ private NodeDefinitionId id;
+
JcrNodeDefinition( JcrSession session,
- NodeType declaringNodeType,
+ JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
boolean autoCreated,
@@ -64,6 +67,18 @@
}
/**
+ * Get the durable identifier for this node definition.
+ *
+ * @return the node definition ID; never null
+ */
+ public NodeDefinitionId getId() {
+ if (id == null) {
+ id = new NodeDefinitionId(declaringNodeType.getInternalName(), name);
+ }
+ return id;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings()
@@ -117,7 +132,7 @@
* @return a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
* <code>declaringNodeType</code>.
*/
- JcrNodeDefinition with( NodeType declaringNodeType ) {
+ JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
return new JcrNodeDefinition(session, declaringNodeType, name, getOnParentVersion(), isAutoCreated(), isMandatory(),
isProtected(), allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypes);
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeType.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -102,43 +102,113 @@
*
* @param propertyName the name of the property for which the definition should be retrieved. Use
* {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
+ * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
+ * is preferred
* @return the property definition for the given name or <code>null</code> if no such definition exists.
* @see JcrNodeType#RESIDUAL_ITEM_NAME
*/
- JcrPropertyDefinition getPropertyDefinition( String propertyName ) {
+ JcrPropertyDefinition getPropertyDefinition( String propertyName,
+ boolean preferMultiValued ) {
+ JcrPropertyDefinition result = null;
for (JcrPropertyDefinition property : propertyDefinitions) {
if (propertyName.equals(property.getName())) {
- return property;
+ result = property;
+ if (property.isMultiple() == preferMultiValued) return result;
+ // Otherwise, keep looking for a better match ...
}
}
for (NodeType nodeType : declaredSupertypes) {
- JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName);
+ JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
+ if (definition != null) {
+ if (definition.isMultiple() == preferMultiValued) return definition;
+ if (result == null) result = definition;
+ }
+ }
+ return result; // may be null
+ }
+
+ /**
+ * Returns the property definition with the given name. This method first checks the property definitions declared within this
+ * type to see if any property definitions have the given name. If no matches are found, this method initiates a recursive
+ * depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the supertypes of
+ * the supertypes).
+ *
+ * @param propertyName the name of the property for which the definition should be retrieved. Use
+ * {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual property definition (if any).
+ * @param preferMultiValued true if the property definition would prefer multiple values, or false if single-valued definition
+ * is preferred
+ * @return the property definition for the given name or <code>null</code> if no such definition exists.
+ * @see JcrNodeType#RESIDUAL_ITEM_NAME
+ */
+ JcrPropertyDefinition getPropertyDefinition( Name propertyName,
+ boolean preferMultiValued ) {
+ JcrPropertyDefinition result = null;
+ for (JcrPropertyDefinition property : propertyDefinitions) {
+ if (propertyName.equals(property.getInternalName())) {
+ result = property;
+ if (property.isMultiple() == preferMultiValued) return result;
+ // Otherwise, keep looking for a better match ...
+ }
+ }
+
+ for (NodeType nodeType : declaredSupertypes) {
+ JcrPropertyDefinition definition = ((JcrNodeType)nodeType).getPropertyDefinition(propertyName, preferMultiValued);
+ if (definition != null) {
+ if (definition.isMultiple() == preferMultiValued) return definition;
+ if (result == null) result = definition;
+ }
+ }
+ return result; // may be null
+ }
+
+ /**
+ * Returns the child node definition with the given name. This method first checks the child node definitions declared within
+ * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
+ * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
+ * supertypes of the supertypes).
+ *
+ * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
+ * {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
+ * @return the child node definition with the given name or <code>null</code> if no such definition exists.
+ * @see JcrNodeType#RESIDUAL_ITEM_NAME
+ * @see #getChildNodeDefinition(Name)
+ */
+ JcrNodeDefinition getChildNodeDefinition( String childDefinitionName ) {
+ for (JcrNodeDefinition childNode : childNodeDefinitions) {
+ if (childDefinitionName.equals(childNode.getName())) {
+ return childNode;
+ }
+ }
+
+ for (NodeType nodeType : declaredSupertypes) {
+ JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
if (definition != null) return definition;
}
return null;
}
/**
- * Returns the node definition for the child node with the given name. This method first checks the child node definitions
- * declared within this type to see if any child node definitions have the given name. If no matches are found, this method
- * initiates a recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or
- * one the supertypes of the supertypes).
+ * Returns the child node definition with the given name. This method first checks the child node definitions declared within
+ * this type to see if any child node definitions have the given name. If no matches are found, this method initiates a
+ * recursive depth first search up the type hierarchy to attempt to find a definition in one of the supertypes (or one the
+ * supertypes of the supertypes).
*
- * @param childNodeName the name of the child node for which the definition should be retrieved. Use
- * {@link JcrNodeType#RESIDUAL_ITEM_NAME} to retrieve the residual child node definition (if any).
+ * @param childDefinitionName the name of the child node definition to be retrieved, or a name containing
+ * {@link JcrNodeType#RESIDUAL_ITEM_NAME '*'} to retrieve the residual child node definition (if any).
* @return the child node definition with the given name or <code>null</code> if no such definition exists.
* @see JcrNodeType#RESIDUAL_ITEM_NAME
+ * @see #getChildNodeDefinition(String)
*/
- JcrNodeDefinition getChildNodeDefinition( String childNodeName ) {
+ JcrNodeDefinition getChildNodeDefinition( Name childDefinitionName ) {
for (JcrNodeDefinition childNode : childNodeDefinitions) {
- if (childNodeName.equals(childNode.getName())) {
+ if (childDefinitionName.equals(childNode.name)) {
return childNode;
}
}
for (NodeType nodeType : declaredSupertypes) {
- JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childNodeName);
+ JcrNodeDefinition definition = ((JcrNodeType)nodeType).getChildNodeDefinition(childDefinitionName);
if (definition != null) return definition;
}
return null;
@@ -153,6 +223,7 @@
* @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
* are not allowed given this node type
*/
+ @Deprecated
JcrNodeDefinition findBestNodeDefinitionForChild( String childName,
String primaryNodeTypeName ) {
// First, try to find a child node definition with the given name
@@ -173,6 +244,46 @@
}
/**
+ * Determine the best (most specific) {@link NodeDefinition} for a child with the supplied name and primary type. If the
+ * primary type is not supplied, then only the name is considered when finding a best match.
+ *
+ * @param childName the name of the child
+ * @param primaryNodeTypeName the name of the primary node type for the child
+ * @return the {@link NodeDefinition} that best matches the child, or null if a child with the supplied name and primary type
+ * are not allowed given this node type
+ */
+ JcrNodeDefinition findBestNodeDefinitionForChild( Name childName,
+ Name primaryNodeTypeName ) {
+ // First, try to find a child node definition with the given name
+ JcrNodeDefinition childNode = getChildNodeDefinition(childName);
+
+ // If there are no named definitions in the type hierarchy, try to find a residual node definition
+ boolean checkResidual = true;
+ if (childNode == null) {
+ childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
+ checkResidual = false;
+ }
+
+ // Check if the node can be added with the named child node definition
+ if (childNode != null && primaryNodeTypeName != null) {
+ NodeType primaryNodeType = getPrimaryNodeType(primaryNodeTypeName);
+ if (primaryNodeType == null) return null;
+ if (!checkTypeAgainstDefinition(primaryNodeType, childNode)) {
+ if (checkResidual) {
+ // Find a residual child definition ...
+ childNode = getChildNodeDefinition(RESIDUAL_ITEM_NAME);
+ if (childNode != null) {
+ // Check the residual child definition ...
+ if (checkTypeAgainstDefinition(primaryNodeType, childNode)) return childNode;
+ }
+ }
+ return null;
+ }
+ }
+ return childNode;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
@@ -211,6 +322,10 @@
}
}
+ protected final NodeType getPrimaryNodeType( Name primaryNodeTypeName ) {
+ return session.nodeTypeManager().getNodeType(primaryNodeTypeName);
+ }
+
/**
* {@inheritDoc}
*
@@ -306,9 +421,9 @@
Value value ) {
CheckArg.isNotNull(propertyName, "propertyName");
- JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+ JcrPropertyDefinition property = getPropertyDefinition(propertyName, false);
if (property == null) {
- property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+ property = getPropertyDefinition(RESIDUAL_ITEM_NAME, false);
}
if (property == null) {
@@ -324,12 +439,11 @@
if (value == null) {
return !property.isMandatory();
}
-
+
try {
assert value instanceof JcrValue : "Illegal implementation of Value interface";
- ((JcrValue) value).asType(property.getRequiredType());
- }
- catch (javax.jcr.ValueFormatException vfe) {
+ ((JcrValue)value).asType(property.getRequiredType());
+ } catch (javax.jcr.ValueFormatException vfe) {
// Cast failed
return false;
}
@@ -345,9 +459,9 @@
Value[] values ) {
CheckArg.isNotNull(propertyName, "propertyName");
- JcrPropertyDefinition property = getPropertyDefinition(propertyName);
+ JcrPropertyDefinition property = getPropertyDefinition(propertyName, true);
if (property == null) {
- property = getPropertyDefinition(RESIDUAL_ITEM_NAME);
+ property = getPropertyDefinition(RESIDUAL_ITEM_NAME, true);
}
if (property == null) {
@@ -368,9 +482,8 @@
if (values[i] != null) {
try {
assert values[i] instanceof JcrValue : "Illegal implementation of Value interface";
- ((JcrValue) values[i]).asType(property.getRequiredType());
- }
- catch (javax.jcr.ValueFormatException vfe) {
+ ((JcrValue)values[i]).asType(property.getRequiredType());
+ } catch (javax.jcr.ValueFormatException vfe) {
// Cast failed
return false;
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrNodeTypeManager.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -96,7 +96,7 @@
return new JcrNodeTypeIterator(mixinNodeTypes.values());
}
- final JcrNodeType getNodeType( Name nodeTypeName ) {
+ JcrNodeType getNodeType( Name nodeTypeName ) {
JcrNodeType nodeType = primaryNodeTypes.get(nodeTypeName);
if (nodeType == null) {
@@ -141,4 +141,32 @@
return null;
}
+ /**
+ * Get the node definition given the supplied identifier.
+ *
+ * @param definitionId the identifier of the node definition
+ * @return the node definition, or null if there is no such definition (or if the ID was null)
+ */
+ JcrNodeDefinition getNodeDefinition( NodeDefinitionId definitionId ) {
+ if (definitionId == null) return null;
+ Name nodeTypeName = definitionId.getNodeTypeName();
+ JcrNodeType nodeType = getNodeType(nodeTypeName);
+ return nodeType.getChildNodeDefinition(definitionId.getChildDefinitionName());
+ }
+
+ /**
+ * Get the property definition given the supplied identifier.
+ *
+ * @param definitionId the identifier of the node definition
+ * @param prefersMultiValued true if the property should be a multi-valued, or false if it should be single-valued
+ * @return the node definition, or null if there is no such definition (or if the ID was null)
+ */
+ JcrPropertyDefinition getPropertyDefinition( PropertyDefinitionId definitionId,
+ boolean prefersMultiValued ) {
+ if (definitionId == null) return null;
+ Name nodeTypeName = definitionId.getNodeTypeName();
+ JcrNodeType nodeType = getNodeType(nodeTypeName);
+ return nodeType.getPropertyDefinition(definitionId.getPropertyDefinitionName(), prefersMultiValued);
+ }
+
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyDefinition.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -24,15 +24,13 @@
package org.jboss.dna.jcr;
import javax.jcr.Value;
-import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.PropertyDefinition;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.property.Name;
-
/**
- * DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
- * through its constructor.
+ * DNA implementation of the {@link PropertyDefinition} interface. This implementation is immutable and has all fields initialized
+ * through its constructor.
*/
@Immutable
class JcrPropertyDefinition extends JcrItemDefinition implements PropertyDefinition {
@@ -41,9 +39,10 @@
private final int requiredType;
private final String[] valueConstraints;
private final boolean multiple;
+ private PropertyDefinitionId id;
JcrPropertyDefinition( JcrSession session,
- NodeType declaringNodeType,
+ JcrNodeType declaringNodeType,
Name name,
int onParentVersion,
boolean autoCreated,
@@ -61,6 +60,18 @@
}
/**
+ * Get the durable identifier for this property definition.
+ *
+ * @return the property definition ID; never null
+ */
+ public PropertyDefinitionId getId() {
+ if (id == null) {
+ id = new PropertyDefinitionId(declaringNodeType.getInternalName(), name);
+ }
+ return id;
+ }
+
+ /**
* {@inheritDoc}
*
* @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues()
@@ -104,7 +115,7 @@
* @return a new <code>JcrPropertyDefinition</code> that is identical to the current object, but with the given
* <code>declaringNodeType</code>.
*/
- JcrPropertyDefinition with( NodeType declaringNodeType ) {
+ JcrPropertyDefinition with( JcrNodeType declaringNodeType ) {
return new JcrPropertyDefinition(this.session, declaringNodeType, this.name, this.getOnParentVersion(),
this.isAutoCreated(), this.isMandatory(), this.isProtected(), this.getDefaultValues(),
this.getRequiredType(), this.getValueConstraints(), this.isMultiple());
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrPropertyIterator.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -31,16 +31,15 @@
import org.jboss.dna.common.util.CheckArg;
/**
- * @author jverhaeg
*/
@Immutable
final class JcrPropertyIterator implements PropertyIterator {
- private final Iterator<Property> iterator;
+ private final Iterator<? extends Property> iterator;
private int ndx;
private int size;
- JcrPropertyIterator( Collection<Property> properties ) {
+ JcrPropertyIterator( Collection<? extends Property> properties ) {
assert properties != null;
iterator = properties.iterator();
size = properties.size();
@@ -88,9 +87,9 @@
* @see javax.jcr.PropertyIterator#nextProperty()
*/
public Property nextProperty() {
- Property property = iterator.next();
+ Property next = iterator.next();
ndx++;
- return property;
+ return next;
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrRootNode.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -23,11 +23,12 @@
*/
package org.jboss.dna.jcr;
+import java.util.UUID;
+import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
-import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.RepositoryException;
import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.graph.Location;
/**
* @author jverhaeg
@@ -35,15 +36,24 @@
@NotThreadSafe
final class JcrRootNode extends AbstractJcrNode {
- JcrRootNode( JcrSession session,
- Location location,
- NodeDefinition nodeDefinition ) {
- super(session, location, nodeDefinition);
+ JcrRootNode( SessionCache cache,
+ UUID nodeUuid ) {
+ super(cache, nodeUuid);
}
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.jcr.AbstractJcrNode#isRoot()
+ */
+ @Override
+ boolean isRoot() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @return 0;
* @see javax.jcr.Item#getDepth()
*/
@@ -91,4 +101,18 @@
public String getPath() {
return "/";
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrItem#getAncestor(int)
+ */
+ @Override
+ public final Item getAncestor( int depth ) throws RepositoryException {
+ if (depth == 0) return this;
+ if (depth < 0) {
+ throw new ItemNotFoundException(JcrI18n.noNegativeDepth.text(depth));
+ }
+ throw new ItemNotFoundException(JcrI18n.tooDeep.text(depth));
+ }
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSession.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -28,11 +28,6 @@
import java.security.AccessControlException;
import java.security.Principal;
import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -42,7 +37,6 @@
import javax.jcr.NamespaceException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
@@ -51,10 +45,6 @@
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
-import javax.jcr.nodetype.NodeType;
-import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.PropertyDefinition;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
@@ -62,20 +52,14 @@
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.connector.RepositorySourceException;
import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Path;
import org.jboss.dna.graph.property.ValueFactories;
-import org.jboss.dna.graph.property.ValueFormatException;
import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
import org.jboss.dna.jcr.JcrNamespaceRegistry.Behavior;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
-import com.google.common.base.ReferenceType;
-import com.google.common.collect.ReferenceMap;
/**
* @author John Verhaeg
@@ -84,12 +68,6 @@
@NotThreadSafe
class JcrSession implements Session {
- /**
- * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be
- * included anyway. This is currently {@value} .
- */
- private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
-
private static final String[] NO_ATTRIBUTES_NAMES = new String[] {};
/**
@@ -114,21 +92,23 @@
protected final ExecutionContext executionContext;
/**
+ * The session-specific attributes that came from the {@link SimpleCredentials}' {@link SimpleCredentials#getAttributeNames()}
+ */
+ private final Map<String, Object> sessionAttributes;
+
+ /**
* The graph representing this session, which uses the {@link #graph session's graph}.
*/
private final Graph graph;
+ private final SessionCache cache;
+
/**
- * The session-specific attributes that came from the {@link SimpleCredentials}' {@link SimpleCredentials#getAttributeNames()}
+ * A cached instance of the root path.
*/
- private final Map<String, Object> sessionAttributes;
+ private final Path rootPath;
- private final ReferenceMap<UUID, AbstractJcrNode> nodesByUuid;
- private final ReferenceMap<String, AbstractJcrNode> nodesByJcrUuid;
private boolean isLive;
- private JcrRootNode rootNode;
- private PropertyDefinition anyMultiplePropertyDefinition;
- private transient org.jboss.dna.graph.property.Property defaultPrimaryType;
JcrSession( JcrRepository repository,
JcrWorkspace workspace,
@@ -147,14 +127,15 @@
NamespaceRegistry local = new LocalNamespaceRegistry(workspaceRegistry);
this.executionContext = workspaceContext.with(local);
this.sessionRegistry = new JcrNamespaceRegistry(Behavior.JSR170_SESSION, local, workspaceRegistry);
+ this.rootPath = this.executionContext.getValueFactories().getPathFactory().createRootPath();
// Set up the graph to use for this session (which uses the session's namespace registry and context) ...
this.graph = Graph.create(this.repository.getRepositorySourceName(),
this.repository.getConnectionFactory(),
this.executionContext);
- this.nodesByUuid = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
- this.nodesByJcrUuid = new ReferenceMap<String, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
+ this.cache = new SessionCache(this, workspace.getName(), this.executionContext, this.workspace.nodeTypeManager(),
+ this.graph);
this.isLive = true;
assert this.repository != null;
@@ -169,24 +150,15 @@
return this.executionContext;
}
- final JcrNodeTypeManager nodeTypeManager() {
+ JcrNodeTypeManager nodeTypeManager() {
return this.workspace.nodeTypeManager();
}
- final NamespaceRegistry namespaces() {
+ NamespaceRegistry namespaces() {
return this.executionContext.getNamespaceRegistry();
}
/**
- * Return an unmodifiable map of nodes given then UUID.
- *
- * @return nodesByUuid
- */
- Map<UUID, AbstractJcrNode> getNodesByUuid() {
- return Collections.unmodifiableMap(nodesByUuid);
- }
-
- /**
* {@inheritDoc}
*
* @see javax.jcr.Session#getWorkspace()
@@ -417,21 +389,6 @@
throw new UnsupportedOperationException();
}
- Node getChild( AbstractJcrNode parent,
- Location location ) throws RepositoryException {
- Node child = null;
- UUID uuid = location.getUuid();
- if (uuid != null) {
- // The location has a UUID, so look up the node by this UUID in the session ...
- child = getNode(uuid);
- }
- if (child == null) {
- // The child was not found in the session's cache, so create the node using its path ...
- child = loadNode(parent, location.getPath());
- }
- return child;
- }
-
/**
* Find or create a JCR Node for the given path. This method works for the root node, too.
*
@@ -441,212 +398,25 @@
* @throws RepositoryException if there is a problem
*/
Node getNode( Path path ) throws RepositoryException, PathNotFoundException {
- if (path.isRoot()) return rootNode();
-
- // Start at the root and walk down the path ...
- AbstractJcrNode parent = rootNode();
- AbstractJcrNode node = null;
- Iterator<Path> pathIter = path.pathsFromRoot();
- while (pathIter.hasNext()) {
- node = loadNode(parent, pathIter.next()); // should throw PathNotFoundException if not there
- parent = node;
- }
- return node;
+ return cache.findJcrNode(null, path.relativeTo(rootPath));
}
/**
- * Find or create a JCR Node for the given path. This method works for the root node, too.
- *
- * @param parent the parent of the node, if known; null if the parent is not known
- * @param path the path; may not be null
- * @return the JCR node instance for the given path; never null
- * @throws PathNotFoundException if the path could not be found
- * @throws RepositoryException if there is a problem
- */
- private AbstractJcrNode loadNode( AbstractJcrNode parent,
- Path path ) throws RepositoryException, PathNotFoundException {
- boolean isRoot = path.isRoot();
- if (isRoot && rootNode != null) return rootNode;
-
- // Get node from source and get it's UUID ...
- org.jboss.dna.graph.Node graphNode = null;
- try {
- graphNode = graph.getNodeAt(path);
- } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
- // If the node isn't found, throw a PathNotFoundException
- throw new PathNotFoundException(JcrI18n.pathNotFound.text(path));
- }
-
- // Now get the DNA node's UUID ...
- Location location = graphNode.getLocation();
- ValueFactories factories = executionContext.getValueFactories();
- UUID uuid = location.getUuid();
- org.jboss.dna.graph.property.Property uuidProperty = null;
- if (uuid != null) {
- // Check for an existing node at this UUID ...
- AbstractJcrNode existing = nodesByUuid.get(uuid);
- if (existing != null) return existing;
-
- // Considered an identification property ...
- uuidProperty = location.getIdProperty(JcrLexicon.UUID);
- if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
- }
- if (uuidProperty == null) {
- uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
- if (uuidProperty != null) {
- // Grab the first 'good' UUID value ...
- for (Object uuidValue : uuidProperty) {
- try {
- uuid = factories.getUuidFactory().create(uuidValue);
- break;
- } catch (ValueFormatException e) {
- // Ignore; just continue with the next property value
- }
- }
- }
- if (uuid == null) {
- // Look for the DNA UUID property ...
- org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
- if (dnaUuidProperty != null) {
- // Grab the first 'good' UUID value ...
- for (Object uuidValue : dnaUuidProperty) {
- try {
- uuid = factories.getUuidFactory().create(uuidValue);
- break;
- } catch (ValueFormatException e) {
- // Ignore; just continue with the next property value
- }
- }
- }
- }
- }
- if (uuid == null) uuid = UUID.randomUUID();
- if (uuidProperty == null) uuidProperty = executionContext.getPropertyFactory().create(JcrLexicon.UUID, uuid);
-
- // See if there is already a JCR node object for this UUID ...
- if (uuid != null && !isRoot) {
- AbstractJcrNode node = getNode(uuid);
- if (node != null) return node;
- }
-
- // Either the UUID is not known, or there was no node. Either way, we have to create the node ...
- if (uuid == null) uuid = UUID.randomUUID();
-
- // Look for the primary type of the node ...
- String primaryTypeNameString = null;
- NamespaceRegistry namespaces = namespaces();
- org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
- if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
- Name primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
- primaryTypeNameString = primaryTypeName.getString(namespaces);
- } else {
- // We have to have a primary type, so use the default ...
- if (defaultPrimaryType == null) {
- defaultPrimaryType = executionContext.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE,
- JcrNtLexicon.UNSTRUCTURED);
- }
- primaryTypeProperty = defaultPrimaryType;
- // We have to add this property to the graph node...
- graphNode.getPropertiesByName().put(primaryTypeProperty.getName(), primaryTypeProperty);
- }
- assert primaryTypeProperty.isEmpty() == false;
-
- // Look for a node definition stored on the node ...
- NodeDefinition definition = null;
- org.jboss.dna.graph.property.Property nodeDefnProperty = graphNode.getProperty(DnaLexicon.NODE_DEFINITON);
- if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
- Path nodeDefnPath = factories.getPathFactory().create(nodeDefnProperty.getFirstValue());
- // Look up the node definition ...
- Name nodeTypeName = nodeDefnPath.getSegment(0).getName();
- JcrNodeType nodeType = nodeTypeManager().getNodeType(nodeTypeName);
- if (nodeType != null && nodeDefnPath.size() > 1) {
- // Look up the definition for the child name rule (in the second segment of the relative path) ...
- String childNameRule = nodeDefnPath.getSegment(1).getString(namespaces);
- definition = nodeType.findBestNodeDefinitionForChild(childNameRule, primaryTypeNameString);
- }
- }
-
- AbstractJcrNode node = null;
- if (isRoot) {
- // The node definition should be set ...
- if (definition == null) {
- definition = nodeTypeManager().getRootNodeDefinition();
- assert definition != null;
- }
-
- // Create the new node ...
- node = new JcrRootNode(this, location, definition);
- } else {
- // Find the parent ...
- if (parent == null) {
- parent = (AbstractJcrNode)getNode(path.getParent());
- }
- assert parent != null;
-
- // Find the node definition for this node ...
- if (definition == null) {
- // Look for the parent's node type, and look for a node definition based upon the name ...
- JcrNodeType nodeType = (JcrNodeType)parent.getPrimaryNodeType();
- String childName = path.getLastSegment().getName().getString(namespaces);
- definition = nodeType.findBestNodeDefinitionForChild(childName, primaryTypeNameString);
- if (definition == null) {
- String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspace.getName());
- throw new RepositorySourceException(msg);
- }
- }
-
- // Now create the node object ...
- node = new JcrNode(this, parent.internalUuid(), location, definition);
- }
-
- // Now populate the node and add to the cache ...
- populateNode(node, graphNode, uuid, uuidProperty, primaryTypeProperty);
- if (isRoot) rootNode = (JcrRootNode)node;
- return node;
- }
-
- AbstractJcrNode getNode( UUID uuid ) {
- return nodesByUuid.get(uuid);
- }
-
- /**
* {@inheritDoc}
*
* @see javax.jcr.Session#getNodeByUUID(java.lang.String)
*/
public Node getNodeByUUID( String uuid ) throws ItemNotFoundException, RepositoryException {
- Node result = null;
- try {
- result = nodesByUuid.get(UUID.fromString(uuid));
- } catch (IllegalArgumentException e) {
- throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
- } catch (Throwable e) {
- throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithUuid.text(uuid, workspace.getName()));
- }
- if (result == null) {
- throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspace.getName()));
- }
- return result;
+ return cache.findJcrNode(UUID.fromString(uuid));
}
- JcrRootNode rootNode() throws RepositoryException {
- // Return cached root node if available
- if (rootNode != null) {
- return rootNode;
- }
- Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
- loadNode(null, rootPath); // sets the root node
- assert rootNode != null;
- return rootNode;
- }
-
/**
* {@inheritDoc}
*
* @see javax.jcr.Session#getRootNode()
*/
public Node getRootNode() throws RepositoryException {
- return rootNode();
+ return cache.findJcrRootNode();
}
/**
@@ -842,187 +612,6 @@
return PropertyType.UNDEFINED;
}
- final void populateNode( AbstractJcrNode node,
- org.jboss.dna.graph.Node graphNode,
- UUID uuid,
- org.jboss.dna.graph.property.Property uuidProperty,
- org.jboss.dna.graph.property.Property primaryTypeProperty ) throws RepositoryException {
- assert node != null;
- assert graphNode != null;
- assert uuid != null;
- assert uuidProperty != null;
- assert primaryTypeProperty != null;
-
- // --------------------------------------------------
- // Create JCR children for corresponding DNA children
- // --------------------------------------------------
- node.setChildren(graphNode.getChildren());
-
- // ------------------------------------------------------
- // Create JCR properties for corresponding DNA properties
- // ------------------------------------------------------
- // First get the property type for each property, based upon the primary type and mixins ...
- // The map with single-valued properties...
- Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
- // ... and the map with multi-valued properties
- Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
-
-
- boolean referenceable = false;
-
- NamespaceRegistry registry = namespaces();
- ValueFactories factories = executionContext.getValueFactories();
- NameFactory nameFactory = factories.getNameFactory();
- NodeTypeManager nodeTypeManager = getWorkspace().getNodeTypeManager();
- List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
- // Start with the primary type ...
- Name primaryTypeName = nameFactory.create(primaryTypeProperty.getFirstValue());
- String primaryTypeNameString = primaryTypeName.getString(registry);
- NodeType primaryType = nodeTypeManager.getNodeType(primaryTypeNameString);
- for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
- String nameString = propertyDefn.getName();
- if ("*".equals(nameString)) {
- anyPropertyDefinitions.add(propertyDefn);
- continue;
- }
- Name name = nameFactory.create(nameString);
-
- if (propertyDefn.isMultiple()) {
- PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
- if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
- }
- else {
- PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
- if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
- }
- }
- // The process the mixin types ...
- org.jboss.dna.graph.property.Property mixinTypesProperty = graphNode.getProperty(JcrLexicon.MIXIN_TYPES);
- if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
- for (Object mixinTypeValue : mixinTypesProperty) {
- Name mixinTypeName = nameFactory.create(mixinTypeValue);
- if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
- String mixinTypeNameString = mixinTypeName.getString(registry);
- NodeType mixinType = nodeTypeManager.getNodeType(mixinTypeNameString);
- for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
- String nameString = propertyDefn.getName();
- if ("*".equals(nameString)) {
- anyPropertyDefinitions.add(propertyDefn);
- continue;
- }
- Name name = nameFactory.create(nameString);
- if (propertyDefn.isMultiple()) {
- PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
- if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
- }
- else {
- PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
- if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
- }
- }
- }
- }
-
- // Now create the JCR property object wrapper around the "jcr:uuid" property ...
- Map<Name, Property> properties = new HashMap<Name, Property>();
- if (referenceable) {
- // We know that this property is single-valued
- PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
- properties.put(JcrLexicon.UUID, new JcrSingleValueProperty(node, propertyDefinition, PropertyType.STRING,
- uuidProperty));
- }
-
- // Now create the JCR property object wrappers around the other properties ...
- for (org.jboss.dna.graph.property.Property dnaProp : graphNode.getProperties()) {
- Name name = dnaProp.getName();
-
- // Skip the JCR and DNA UUID properties (using the EXACT Name instances on the Property) ...
- if (JcrLexicon.UUID.equals(name) || DnaLexicon.UUID.equals(name)) continue;
-
- // Figure out the JCR property type for this property ...
- PropertyDefinition propertyDefinition;
-
- if (dnaProp.isMultiple()) {
- propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
- }
- else {
- propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);
-
- // If the property has only one value, dnaProp.isMultiple() will return false, but the
- // property may actually be a multi-valued property that happens to have one property set.
- if (propertyDefinition == null) {
- propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
- }
- }
-
- // If no property type was found for this property, see if there is a wildcard property ...
- if (propertyDefinition == null) {
- for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext(); ) {
- PropertyDefinition nextDef = iter.next();
-
- // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
- if ((nextDef.isMultiple() && dnaProp.isMultiple())
- || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
- propertyDefinition = nextDef;
- break;
- }
- }
- }
-
-
- // If there still is no property type defined ...
- if (propertyDefinition == null) {
- assert anyPropertyDefinitions.isEmpty();
- if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
- // We can use the "nt:unstructured" property definitions for any property ...
- if (anyMultiplePropertyDefinition == null) {
- String unstructuredName = JcrNtLexicon.UNSTRUCTURED.getString(registry);
- NodeType unstructured = nodeTypeManager.getNodeType(unstructuredName);
- for (PropertyDefinition definition : unstructured.getDeclaredPropertyDefinitions()) {
- if (definition.isMultiple()) {
- anyMultiplePropertyDefinition = definition;
- }
- }
- }
- propertyDefinition = anyMultiplePropertyDefinition;
- }
- }
- if (propertyDefinition == null) {
- // We're supposed to skip this property (since we don't have a definition for it) ...
- continue;
- }
-
- // Figure out if this is a multi-valued property ...
- boolean isMultiple = propertyDefinition.isMultiple();
- if (!isMultiple && dnaProp.isEmpty()) {
- // Only multi-valued properties can have no values; so if not multi-valued, then skip ...
- continue;
- }
-
- // Figure out the property type ...
- int propertyType = propertyDefinition.getRequiredType();
- if (propertyType == PropertyType.UNDEFINED) {
- propertyType = jcrPropertyTypeFor(dnaProp);
- }
-
- // Create the appropriate JCR property wrapper ...
- if (isMultiple) {
- properties.put(name, new JcrMultiValueProperty(node, propertyDefinition, propertyType, dnaProp));
- } else {
- properties.put(name, new JcrSingleValueProperty(node, propertyDefinition, propertyType, dnaProp));
- }
- }
-
- // Now set the properties on the node ...
- node.setProperties(properties);
-
- // Set node's UUID, creating one if necessary
- nodesByJcrUuid.put(uuid.toString(), node);
- node.setInternalUuid(uuid);
- // Setup node to be retrieved by DNA UUID
- nodesByUuid.put(uuid, node);
- }
-
/**
* {@inheritDoc}
*
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSingleValueProperty.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -30,9 +30,7 @@
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.PropertyDefinition;
import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.Property;
import org.jboss.dna.graph.property.Reference;
import org.jboss.dna.graph.property.ValueFactories;
@@ -41,11 +39,9 @@
*/
final class JcrSingleValueProperty extends AbstractJcrProperty {
- JcrSingleValueProperty( AbstractJcrNode node,
- PropertyDefinition definition,
- int propertyType,
- Property dnaProperty ) {
- super(node, definition, propertyType, dnaProperty);
+ JcrSingleValueProperty( SessionCache cache,
+ PropertyId propertyId ) {
+ super(cache, propertyId);
}
/**
@@ -55,7 +51,7 @@
*/
public boolean getBoolean() throws RepositoryException {
try {
- return getExecutionContext().getValueFactories().getBooleanFactory().create(getDnaProperty().getFirstValue());
+ return context().getValueFactories().getBooleanFactory().create(property().getFirstValue());
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -68,10 +64,7 @@
*/
public Calendar getDate() throws RepositoryException {
try {
- return getExecutionContext().getValueFactories()
- .getDateFactory()
- .create(getDnaProperty().getFirstValue())
- .toCalendar();
+ return context().getValueFactories().getDateFactory().create(property().getFirstValue()).toCalendar();
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -84,7 +77,7 @@
*/
public double getDouble() throws RepositoryException {
try {
- return getExecutionContext().getValueFactories().getDoubleFactory().create(getDnaProperty().getFirstValue());
+ return context().getValueFactories().getDoubleFactory().create(property().getFirstValue());
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -96,7 +89,7 @@
* @see javax.jcr.Property#getLength()
*/
public long getLength() throws RepositoryException {
- return createValue(getDnaProperty().getFirstValue()).getLength();
+ return createValue(property().getFirstValue()).getLength();
}
/**
@@ -116,7 +109,7 @@
*/
public long getLong() throws RepositoryException {
try {
- return getExecutionContext().getValueFactories().getLongFactory().create(getDnaProperty().getFirstValue());
+ return context().getValueFactories().getLongFactory().create(property().getFirstValue());
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -129,10 +122,10 @@
*/
public final Node getNode() throws RepositoryException {
try {
- ValueFactories factories = getExecutionContext().getValueFactories();
- Reference dnaReference = factories.getReferenceFactory().create(getDnaProperty().getFirstValue());
+ ValueFactories factories = context().getValueFactories();
+ Reference dnaReference = factories.getReferenceFactory().create(property().getFirstValue());
UUID uuid = factories.getUuidFactory().create(dnaReference);
- return ((JcrSession)getSession()).getNode(uuid);
+ return cache.findJcrNode(uuid);
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -145,7 +138,7 @@
*/
public InputStream getStream() throws RepositoryException {
try {
- Binary binary = getExecutionContext().getValueFactories().getBinaryFactory().create(getDnaProperty().getFirstValue());
+ Binary binary = context().getValueFactories().getBinaryFactory().create(property().getFirstValue());
return new SelfClosingInputStream(binary);
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
@@ -159,7 +152,7 @@
*/
public String getString() throws RepositoryException {
try {
- return getExecutionContext().getValueFactories().getStringFactory().create(getDnaProperty().getFirstValue());
+ return context().getValueFactories().getStringFactory().create(property().getFirstValue());
} catch (org.jboss.dna.graph.property.ValueFormatException e) {
throw new ValueFormatException(e.getMessage(), e);
}
@@ -170,8 +163,8 @@
*
* @see javax.jcr.Property#getValue()
*/
- public Value getValue() {
- return createValue(getDnaProperty().getFirstValue());
+ public Value getValue() throws RepositoryException {
+ return createValue(property().getFirstValue());
}
/**
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrSystemViewExporter.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -114,7 +114,7 @@
AbstractJcrProperty prop = (AbstractJcrProperty)property;
- Name propertyName = prop.getDnaProperty().getName();
+ Name propertyName = prop.name();
if (SPECIAL_PROPERTY_NAMES.contains(propertyName)) {
return;
}
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrWorkspace.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -148,10 +148,10 @@
this.session = new JcrSession(this.repository, this, this.context, sessionAttributes);
// This must be initialized after the session
- this.nodeTypeManager = new JcrNodeTypeManager(this.session,
- new DnaBuiltinNodeTypeSource(this.session,
- new JcrBuiltinNodeTypeSource(this.session)));
-
+ JcrNodeTypeSource source = null;
+ source = new JcrBuiltinNodeTypeSource(this.session);
+ source = new DnaBuiltinNodeTypeSource(this.session, source);
+ this.nodeTypeManager = new JcrNodeTypeManager(this.session, source);
}
final String getSourceName() {
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,164 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.io.Serializable;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+
+/**
+ * An immutable identifier for a node definition. Although instances can be serialized, the node definitions are often stored
+ * within the graph as {@link #getString() string values} on a property. These string values can later be
+ * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
+ * use namespace prefixes, so they are long-lasting and durable.
+ */
+@Immutable
+public final class NodeDefinitionId implements Serializable {
+
+ /**
+ * Current version is {@value} .
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The string-form of the name that can be used to represent a residual property definition.
+ */
+ public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+ private final Name nodeTypeName;
+ private final Name childDefinitionName;
+ private final int hc;
+
+ /**
+ * Create an identifier for a node definition.
+ *
+ * @param nodeTypeName the name of the node type on which this child node definition is defined; may not be null
+ * @param childDefinitionName the name of the child node definition, which may be a {@link #ANY_NAME residual child
+ * definition}; may not be null
+ */
+ public NodeDefinitionId( Name nodeTypeName,
+ Name childDefinitionName ) {
+ assert nodeTypeName != null;
+ assert childDefinitionName != null;
+ this.nodeTypeName = nodeTypeName;
+ this.childDefinitionName = childDefinitionName;
+ this.hc = HashCode.compute(this.nodeTypeName, this.childDefinitionName);
+ }
+
+ /**
+ * Get the name of the node type on which the child node definition is defined.
+ *
+ * @return the node type's name; never null
+ */
+ public Name getNodeTypeName() {
+ return nodeTypeName;
+ }
+
+ /**
+ * Get the name of the child definition.
+ *
+ * @return the child definition's name; never null
+ */
+ public Name getChildDefinitionName() {
+ return childDefinitionName;
+ }
+
+ /**
+ * Determine whether this node definition defines any named child.
+ *
+ * @return true if this node definition allows children with any name, or false if this definition requires a particular child
+ * name
+ */
+ public boolean allowsAnyChildName() {
+ return childDefinitionName.getLocalName().equals(ANY_NAME) && childDefinitionName.getNamespaceUri().length() == 0;
+ }
+
+ /**
+ * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
+ *
+ * @return the string form
+ */
+ public String getString() {
+ return this.nodeTypeName.getString() + '/' + this.childDefinitionName.getString();
+ }
+
+ /**
+ * Parse the supplied string for of an identifer, and return the object form for that identifier.
+ *
+ * @param definition the {@link #getString() string form of the identifier}; may not be null
+ * @param factory the factory that should be used to create Name objects; may not be null
+ * @return the object form of the identifier; never null
+ * @throws ValueFormatException if the definition is not the valid format
+ */
+ public static NodeDefinitionId fromString( String definition,
+ NameFactory factory ) {
+ int index = definition.indexOf('/');
+ String nodeTypeNameString = definition.substring(0, index);
+ String childDefinitionNameString = definition.substring(index + 1);
+ Name nodeTypeName = factory.create(nodeTypeNameString);
+ Name childDefinitionName = factory.create(childDefinitionNameString);
+ return new NodeDefinitionId(nodeTypeName, childDefinitionName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NodeDefinitionId) {
+ NodeDefinitionId that = (NodeDefinitionId)obj;
+ if (this.hc != that.hc) return false;
+ if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
+ return this.childDefinitionName.equals(that.childDefinitionName);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.nodeTypeName.toString() + '/' + this.childDefinitionName.toString();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/NodeDefinitionId.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,162 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.io.Serializable;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+
+/**
+ * An immutable identifier for a property definition. Although instances can be serialized, the property definitions are often
+ * stored within the graph as {@link #getString() string values} on a property. These string values can later be
+ * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
+ * use namespace prefixes, so they are long-lasting and durable.
+ */
+@Immutable
+public final class PropertyDefinitionId implements Serializable {
+
+ /**
+ * Current version is {@value} .
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The string-form of the name that can be used to represent a residual property definition.
+ */
+ public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
+
+ private final Name nodeTypeName;
+ private final Name propertyDefinitionName;
+ private final int hc;
+
+ /**
+ * Create a new identifier for a propety definition.
+ *
+ * @param nodeTypeName the name of the node type; may not be null
+ * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may
+ * not be null
+ */
+ public PropertyDefinitionId( Name nodeTypeName,
+ Name propertyDefinitionName ) {
+ this.nodeTypeName = nodeTypeName;
+ this.propertyDefinitionName = propertyDefinitionName;
+ this.hc = HashCode.compute(this.nodeTypeName, this.propertyDefinitionName);
+ }
+
+ /**
+ * Get the name of the node type on which the property definition is defined
+ *
+ * @return the node type's name; may not be null
+ */
+ public Name getNodeTypeName() {
+ return nodeTypeName;
+ }
+
+ /**
+ * Get the name of the property definition.
+ *
+ * @return the property definition's name; never null
+ */
+ public Name getPropertyDefinitionName() {
+ return propertyDefinitionName;
+ }
+
+ /**
+ * Determine whether this property definition allows properties with any name.
+ *
+ * @return true if this node definition allows properties with any name, or false if this definition requires a particular
+ * property name
+ */
+ public boolean allowsAnyChildName() {
+ return propertyDefinitionName.getLocalName().equals(ANY_NAME) && propertyDefinitionName.getNamespaceUri().length() == 0;
+ }
+
+ /**
+ * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
+ *
+ * @return the string form
+ */
+ public String getString() {
+ return this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString();
+ }
+
+ /**
+ * Parse the supplied string for of an identifer, and return the object form for that identifier.
+ *
+ * @param definition the {@link #getString() string form of the identifier}; may not be null
+ * @param factory the factory that should be used to create Name objects; may not be null
+ * @return the object form of the identifier; never null
+ * @throws ValueFormatException if the definition is not the valid format
+ */
+ public static PropertyDefinitionId fromString( String definition,
+ NameFactory factory ) {
+ int index = definition.indexOf('/');
+ String nodeTypeNameString = definition.substring(0, index);
+ String propertyDefinitionNameString = definition.substring(index + 1);
+ Name nodeTypeName = factory.create(nodeTypeNameString);
+ Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
+ return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertyDefinitionId) {
+ PropertyDefinitionId that = (PropertyDefinitionId)obj;
+ if (this.hc != that.hc) return false;
+ if (!this.nodeTypeName.equals(that.nodeTypeName)) return false;
+ return this.propertyDefinitionName.equals(that.propertyDefinitionName);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.nodeTypeName.toString() + '/' + this.propertyDefinitionName.toString();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyDefinitionId.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,111 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.property.Name;
+
+/**
+ * An immutable identifier for a property, often used to reference information a property held within the {@link SessionCache}.
+ */
+@Immutable
+public final class PropertyId {
+
+ private final UUID nodeId;
+ private final Name propertyName;
+ private final int hc;
+
+ /**
+ * Create a new property identifier.
+ *
+ * @param nodeId the UUID of the node that owns the property being reference; may not be null
+ * @param propertyName the name of the property being referenced; may not be null
+ */
+ public PropertyId( UUID nodeId,
+ Name propertyName ) {
+ assert nodeId != null;
+ assert propertyName != null;
+ this.nodeId = nodeId;
+ this.propertyName = propertyName;
+ this.hc = HashCode.compute(this.nodeId, this.propertyName);
+ }
+
+ /**
+ * Get the UUID of the node that owns the property.
+ *
+ * @return the node's UUID; never null
+ */
+ public UUID getNodeId() {
+ return nodeId;
+ }
+
+ /**
+ * Get the name of the property.
+ *
+ * @return the property name; never null
+ */
+ public Name getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertyId) {
+ PropertyId that = (PropertyId)obj;
+ if (this.hc != that.hc) return false;
+ if (!this.nodeId.equals(that.nodeId)) return false;
+ return this.propertyName.equals(that.propertyName);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.nodeId.toString() + '@' + this.propertyName.toString();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/PropertyId.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java (rev 0)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,1583 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+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.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.PropertyDefinition;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Results;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+import org.jboss.dna.graph.property.ValueFormatException;
+import com.google.common.base.ReferenceType;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ReferenceMap;
+
+/**
+ * The class that manages the session's information that has been locally-cached after reading from the underlying {@link Graph
+ * repository} or modified by the session but not yet saved or commited to the repository.
+ * <p>
+ * The cached information is broken into several different categories that are each described below.
+ * </p>
+ * <h3>JCR API objects</h3>
+ * <p>
+ * Clients using the DNA JCR implementation obtain a {@link JcrSession JCR Session} (which generally owns this cache instance) as
+ * well as the JCR {@link JcrNode Node} and {@link AbstractJcrProperty Property} instances. This cache ensures that the same JCR
+ * Node or Property objects are always returned for the same item in the repository, ensuring that the "==" operator always holds
+ * true for the same item. However, as soon as all (client) references to these objects are garbage collected, this class is free
+ * to also release those objects and, when needed, recreate new implementation objects.
+ * </p>
+ * <p>
+ * This approach helps reduce memory utilization since any unused items are available for garbage collection, but it also
+ * guarantees that once a client maintains a reference to an item, the same Java object will always be used for any references to
+ * that item.
+ * </p>
+ * <h3>Cached nodes</h3>
+ * <p>
+ * The session cache is also responsible for maintaining a local cache of node information retrieved from the underlying
+ * repository, reducing the need to request information any more than necessary. This information includes that obtained directly
+ * from the repository store, including node properties, children, and references to the parent. It also includes computed
+ * information, such as the NodeDefinition for a node, the name of the primary type and mixin types, and the original
+ * {@link Location} of the node in the repository.
+ * </p>
+ * <h3>Transient changes</h3>
+ * <p>
+ * Any time content is changed in the session, those changes are held within the session until they are saved either by
+ * {@link Session#save() saving the session} or {@link Item#save() saving an individual item} (which includes any content below
+ * that item). This cache maintains all these transient changes, and when requested will send the change requests down the
+ * repository. At any point, these transient changes may be rolled back (or "released"), again either for the
+ * {@link Session#refresh(boolean) whole session} or for {@link Item#refresh(boolean) individual items}.
+ * </p>
+ */
+@ThreadSafe
+public class SessionCache {
+
+ /**
+ * Hidden flag that controls whether properties that appear on DNA nodes but not allowed by the node type or mixins should be
+ * included anyway. This is currently {@value} .
+ */
+ private static final boolean INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS = true;
+
+ private final JcrSession session;
+ private final String workspaceName;
+ protected final ExecutionContext context;
+ protected final PathFactory pathFactory;
+ private final NameFactory nameFactory;
+ private final ValueFactory<String> stringFactory;
+ private final NamespaceRegistry namespaces;
+ private final PropertyFactory propertyFactory;
+ private final Graph store;
+ private final Name defaultPrimaryTypeName;
+ private final Property defaultPrimaryTypeProperty;
+ private final Path rootPath;
+
+ private UUID root;
+ private final ReferenceMap<UUID, AbstractJcrNode> jcrNodes;
+ private final ReferenceMap<PropertyId, AbstractJcrProperty> jcrProperties;
+
+ private final HashMap<UUID, NodeInfo> cachedNodes;
+ private final HashMap<UUID, NodeInfo> changedNodes;
+
+ public SessionCache( JcrSession session,
+ String workspaceName,
+ ExecutionContext context,
+ JcrNodeTypeManager nodeTypes,
+ Graph store ) {
+ assert session != null;
+ assert workspaceName != null;
+ assert context != null;
+ assert store != null;
+ this.session = session;
+ this.workspaceName = workspaceName;
+ this.store = store;
+ this.context = context;
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.nameFactory = context.getValueFactories().getNameFactory();
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.namespaces = context.getNamespaceRegistry();
+ this.propertyFactory = context.getPropertyFactory();
+ this.defaultPrimaryTypeName = JcrNtLexicon.UNSTRUCTURED;
+ this.defaultPrimaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, this.defaultPrimaryTypeName);
+ this.rootPath = pathFactory.createRootPath();
+
+ this.jcrNodes = new ReferenceMap<UUID, AbstractJcrNode>(ReferenceType.STRONG, ReferenceType.SOFT);
+ this.jcrProperties = new ReferenceMap<PropertyId, AbstractJcrProperty>(ReferenceType.STRONG, ReferenceType.SOFT);
+
+ this.cachedNodes = new HashMap<UUID, NodeInfo>();
+ this.changedNodes = new HashMap<UUID, NodeInfo>();
+ }
+
+ JcrSession session() {
+ return session;
+ }
+
+ ExecutionContext context() {
+ return context;
+ }
+
+ String workspaceName() {
+ return workspaceName;
+ }
+
+ JcrNodeTypeManager nodeTypes() {
+ return session.nodeTypeManager();
+ }
+
+ public JcrRootNode findJcrRootNode() throws RepositoryException {
+ return (JcrRootNode)findJcrNode(findNodeInfoForRoot().getUuid());
+ }
+
+ /**
+ * Find the JCR {@link JcrNode Node implementation} for the given UUID.
+ *
+ * @param uuid the node's UUID
+ * @return the existing node implementation
+ * @throws ItemNotFoundException if a node with the supplied UUID could not be found
+ * @throws RepositoryException if an error resulting in finding this node in the repository
+ */
+ public AbstractJcrNode findJcrNode( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+ AbstractJcrNode node = jcrNodes.get(uuid);
+ if (node != null) return node;
+
+ // An existing JCR Node object was not found, so we'll have to create it by finding the underlying
+ // NodeInfo for the node (from the changed state or the cache) ...
+ NodeInfo info = findNodeInfo(uuid);
+ assert info != null;
+
+ // Create the appropriate JCR Node object ...
+ return createAndCacheJcrNodeFor(info);
+ }
+
+ /**
+ * Find the JCR {@link AbstractJcrNode Node implementation} for the node given by the UUID of a reference node and a relative
+ * path from the reference node. The relative path should already have been {@link Path#getNormalizedPath() normalized}.
+ *
+ * @param uuidOfReferenceNode the UUID of the reference node; may be null if the root node is to be used as the reference
+ * @param relativePath the relative (but normalized) path from the reference node, but which may be an absolute (and
+ * normalized) path only when the reference node is the root node; may not be null
+ * @return the information for the referenced node; never null
+ * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
+ * @throws PathNotFoundException if the node given by the relative path does not exist
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ * @see #findNodeInfoForRoot()
+ */
+ public AbstractJcrNode findJcrNode( UUID uuidOfReferenceNode,
+ Path relativePath ) throws PathNotFoundException, RepositoryException {
+ // An existing JCR Node object was not found, so we'll have to create it by finding the underlying
+ // NodeInfo for the node (from the changed state or the cache) ...
+ NodeInfo info = findNodeInfo(uuidOfReferenceNode, relativePath);
+ assert info != null;
+
+ // Look for an existing node ...
+ AbstractJcrNode node = jcrNodes.get(info.getUuid());
+ if (node != null) return node;
+
+ // Create the appropriate JCR Node object ...
+ return createAndCacheJcrNodeFor(info);
+ }
+
+ public AbstractJcrProperty findJcrProperty( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+ AbstractJcrProperty property = jcrProperties.get(propertyId);
+ if (property != null) return property;
+
+ // An existing JCR Property object was not found, so we'll have to create it by finding the underlying
+ // NodeInfo for the property's parent (from the changed state or the cache) ...
+ PropertyInfo info = findPropertyInfo(propertyId); // throws PathNotFoundException if node not there
+ if (info == null) return null; // no such property on this node
+
+ // Now create the appropriate JCR Property object ...
+ return createAndCacheJcrPropertyFor(info);
+ }
+
+ public Collection<AbstractJcrProperty> findJcrPropertiesFor( UUID nodeUuid )
+ throws ItemNotFoundException, RepositoryException {
+ NodeInfo info = findNodeInfo(nodeUuid);
+ Map<Name, PropertyInfo> properties = info.getProperties();
+ Collection<AbstractJcrProperty> result = new ArrayList<AbstractJcrProperty>(properties.size());
+ for (PropertyInfo propertyInfo : properties.values()) {
+ result.add(findJcrProperty(propertyInfo.getPropertyId()));
+ }
+ return result;
+ }
+
+ /**
+ * Find the JCR {@link AbstractJcrItem Item implementation} for the node or property given by the UUID of a reference node and
+ * a relative path from the reference node to the desired item. The relative path should already have been
+ * {@link Path#getNormalizedPath() normalized}.
+ *
+ * @param uuidOfReferenceNode the UUID of the reference node; may be null if the root node is to be used as the reference
+ * @param relativePath the relative (but normalized) path from the reference node to the desired item, but which may be an
+ * absolute (and normalized) path only when the reference node is the root node; may not be null
+ * @return the information for the referenced item; never null
+ * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist, or if an item given by the
+ * supplied relative path does not exist
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ * @see #findNodeInfoForRoot()
+ */
+ public AbstractJcrItem findJcrItem( UUID uuidOfReferenceNode,
+ Path relativePath ) throws ItemNotFoundException, RepositoryException {
+ // A pathological case is an empty relative path ...
+ if (relativePath.size() == 0) {
+ return findJcrNode(uuidOfReferenceNode);
+ }
+ if (relativePath.size() == 1) {
+ Path.Segment segment = relativePath.getLastSegment();
+ if (segment.isSelfReference()) return findJcrNode(uuidOfReferenceNode);
+ if (segment.isParentReference()) {
+ NodeInfo referencedNode = findNodeInfo(uuidOfReferenceNode);
+ return findJcrNode(referencedNode.getParent());
+ }
+ }
+
+ // Peek into the last segment of the path to see whether it uses a SNS index (and it's > 1) ...
+ Path.Segment lastSegment = relativePath.getLastSegment();
+ if (lastSegment.getIndex() > 1) {
+ // Only nodes can have SNS index (but an index of 1 is the default)...
+ return findJcrNode(uuidOfReferenceNode);
+ }
+
+ NodeInfo parent = null;
+ if (relativePath.size() == 1) {
+ // The referenced node must be the parent ...
+ parent = findNodeInfo(uuidOfReferenceNode);
+ } else {
+ // We know that the parent of the referenced item should be a node (if the path is right) ...
+ parent = findNodeInfo(uuidOfReferenceNode, relativePath.getParent());
+ }
+
+ // JSR-170 doesn't allow children and proeprties to have the same name, but this is relaxed in JSR-283.
+ // But JSR-283 Section 3.3.4 states "The method Session.getItem will return the item at the specified path
+ // if there is only one such item, if there is both a node and a property at the specified path, getItem
+ // will return the node." Therefore, look for a child first ...
+ ChildNode child = parent.getChildren().getChild(lastSegment);
+ if (child != null) {
+ return findJcrNode(child.getUuid());
+ }
+
+ // Otherwise it should be a property ...
+ PropertyInfo propertyInfo = parent.getProperty(lastSegment.getName());
+ if (propertyInfo != null) {
+ return findJcrProperty(propertyInfo.getPropertyId());
+ }
+
+ // It was not found, so prepare a good exception message ...
+ String msg = null;
+ if (findNodeInfoForRoot().getUuid().equals(uuidOfReferenceNode)) {
+ // The reference node was the root, so use this fact to convert the path to an absolute path in the message
+ Path absolutePath = rootPath.resolve(relativePath);
+ msg = JcrI18n.itemNotFoundAtPath.text(absolutePath, workspaceName);
+ } else {
+ // Find the path of the reference node ...
+ Path referenceNodePath = getPathFor(uuidOfReferenceNode);
+ msg = JcrI18n.itemNotFoundAtPathRelativeToReferenceNode.text(relativePath, referenceNodePath, workspaceName);
+ }
+ throw new ItemNotFoundException(msg);
+ }
+
+ /**
+ * Utility method that creates and caches the appropriate kind of AbstractJcrNode implementation for node given by the
+ * supplied information.
+ *
+ * @param info the information for the node; may not be null
+ * @return the <i>new</i> instance of the {@link Node}; never null
+ */
+ private AbstractJcrNode createAndCacheJcrNodeFor( NodeInfo info ) {
+ UUID uuid = info.getUuid();
+ Location location = info.getOriginalLocation();
+ NodeDefinitionId nodeDefinitionId = info.getDefinitionId();
+ JcrNodeDefinition definition = nodeTypes().getNodeDefinition(nodeDefinitionId);
+ assert definition != null;
+
+ if (root == null) {
+ // Need to determine if this is the root node ...
+ if (location.getPath().isRoot()) {
+ // It is a root node ...
+ JcrRootNode node = new JcrRootNode(this, uuid);
+ jcrNodes.put(uuid, node);
+ root = uuid;
+ return node;
+ }
+ }
+
+ // It is not a root node ...
+ JcrNode node = new JcrNode(this, uuid);
+ jcrNodes.put(uuid, node);
+ return node;
+ }
+
+ /**
+ * Utility method that creates and caches the appropriate kind of AbstractJcrProperty implementation for property given by the
+ * supplied information.
+ *
+ * @param info the information for the property; may not be null
+ * @return the <i>new</i> instance of the {@link Property}; never null
+ */
+ private AbstractJcrProperty createAndCacheJcrPropertyFor( PropertyInfo info ) {
+ boolean multiValued = info.getProperty().isMultiple();
+ JcrPropertyDefinition definition = nodeTypes().getPropertyDefinition(info.getDefinitionId(), multiValued);
+ assert definition != null;
+ if (multiValued) {
+ return new JcrMultiValueProperty(this, info.getPropertyId());
+ }
+ return new JcrSingleValueProperty(this, info.getPropertyId());
+ }
+
+ // public AbstractJcrProperty findJcrProperty( Path propertyPath ) throws PathNotFoundException, RepositoryException {
+ // if (propertyPath.isRoot()) return null;
+ // Path nodePath = propertyPath.getParent();
+ // NodeInfo nodeInfo = findNodeInfo(nodePath);
+ // Name propertyName = propertyPath.getLastSegment().getName();
+ // PropertyInfo info = nodeInfo.getProperty(propertyName);
+ //
+ // // Look for an existing JCR Property object ...
+ // PropertyId propertyId = info.getPropertyId();
+ // AbstractJcrProperty property = jcrProperties.get(propertyId);
+ // if (property != null) return property;
+ //
+ // // Now create the appropriate JCR Property object ...
+ // return createAndCacheJcrPropertyFor(info);
+ // }
+
+ /**
+ * Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
+ * especially after the information has been cached. Note, however, that this method first checks the cache, and if the
+ * information is not in the cache, the information is read from the repository.
+ *
+ * @param uuid the UUID for the node; may not be null
+ * @return the information for the node with the supplied UUID, or null if the information is not in the cache
+ * @throws ItemNotFoundException if there is no node with the supplied UUID
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ * @see #findNodeInfoInCache(UUID)
+ * @see #findNodeInfo(UUID, Path)
+ * @see #findNodeInfoForRoot()
+ */
+ NodeInfo findNodeInfo( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+ // See if we already have something in the changed nodes ...
+ NodeInfo info = changedNodes.get(uuid);
+ if (info == null) {
+ // Or in the cache ...
+ info = cachedNodes.get(uuid);
+ if (info == null) {
+ info = loadFromGraph(uuid, null);
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Find the information for the node given by the UUID of the node. This is often the fastest way to find information,
+ * especially after the information has been cached. Note, however, that this method only checks the cache.
+ *
+ * @param uuid the UUID for the node; may not be null
+ * @return the information for the node with the supplied UUID, or null if the information is not in the cache
+ * @see #findNodeInfo(UUID)
+ * @see #findNodeInfo(UUID, Path)
+ * @see #findNodeInfoForRoot()
+ */
+ NodeInfo findNodeInfoInCache( UUID uuid ) {
+ // See if we already have something in the changed nodes ...
+ NodeInfo info = changedNodes.get(uuid);
+ if (info == null) {
+ // Or in the cache ...
+ info = cachedNodes.get(uuid);
+ }
+ return info;
+ }
+
+ /**
+ * Find the information for the root node. Generally, this returns information that's in the cache, except for the first time
+ * the root node is needed.
+ *
+ * @return the node information
+ * @throws RepositoryException if there is an error while obtaining the information from the repository
+ */
+ NodeInfo findNodeInfoForRoot() throws RepositoryException {
+ if (root == null) {
+ // We haven't found the root yet ...
+ NodeInfo info = loadFromGraph(this.rootPath, null);
+ root = info.getUuid();
+ this.jcrNodes.put(root, new JcrRootNode(this, root));
+ return info;
+ }
+ return findNodeInfo(root);
+ }
+
+ /**
+ * Find the information for the node given by the UUID of a reference node and a relative path from the reference node.
+ *
+ * @param node the reference node; may be null if the root node is to be used as the reference
+ * @param relativePath the relative path from the reference node, but which may be an absolute path only when the reference
+ * node is the root node; may not be null
+ * @return the information for the referenced node; never null
+ * @throws ItemNotFoundException if the reference node with the supplied UUID does not exist
+ * @throws PathNotFoundException if the node given by the relative path does not exist
+ * @throws RepositoryException if any other error occurs while reading information from the repository
+ * @see #findNodeInfoForRoot()
+ */
+ NodeInfo findNodeInfo( UUID node,
+ Path relativePath ) throws ItemNotFoundException, PathNotFoundException, RepositoryException {
+ // The relative path must be normalized ...
+ assert relativePath.isNormalized();
+
+ // Find the node from which we're starting ...
+ NodeInfo fromInfo = null;
+ if (node == null) {
+ // This is only valid when the path is relative to the root (or it's an absolute path)
+ fromInfo = findNodeInfoForRoot();
+ node = fromInfo.getUuid();
+ } else {
+ fromInfo = findNodeInfo(node);
+ assert relativePath.isAbsolute() ? node == root : true;
+ }
+ if (relativePath.isAbsolute()) {
+ relativePath = relativePath.relativeTo(this.rootPath);
+ }
+
+ // If the relative path is of zero-length ...
+ if (relativePath.size() == 0) {
+ return fromInfo;
+ }
+ // Or it is of length 1 but it is a self reference ...
+ if (relativePath.size() == 1 && relativePath.getLastSegment().isSelfReference()) {
+ return fromInfo;
+ }
+
+ // TODO: This could be more efficient than always walking the path. For example, we could
+ // maintain a cache of paths. Right now, we are walking as much of the path as we can,
+ // but as soon as we reach the bottom-most cached/changed node, we need to read the rest
+ // from the graph. We are figuring out all of the remaining nodes and read them from
+ // the graph in one batch operation, so that part is pretty good.
+
+ // Now, walk the path to find the nodes, being sure to look for changed information ...
+ NodeInfo info = fromInfo;
+ Iterator<Path.Segment> pathsIter = relativePath.iterator();
+ while (pathsIter.hasNext()) {
+ Path.Segment child = pathsIter.next();
+ if (child.isParentReference()) {
+ // Walk up ...
+ UUID parentUuid = info.getParent();
+ if (parentUuid == null) {
+ assert info.getUuid() == findNodeInfoForRoot().getUuid();
+ String msg = JcrI18n.errorWhileFindingNodeWithPath.text(relativePath, workspaceName);
+ throw new PathNotFoundException(msg);
+ }
+ info = findNodeInfo(parentUuid);
+ } else {
+ // Walk down ...
+ // Note that once we start walking down, a normalized path should never have any more parent
+ // or self references
+ ChildNode childNodeInfo = info.getChildren().getChild(child);
+ if (childNodeInfo == null) {
+ // The node (no longer?) exists, so compute the
+ Path fromPath = getPathFor(fromInfo);
+ String msg = JcrI18n.pathNotFoundRelativeTo.text(relativePath, fromPath, workspaceName);
+ throw new PathNotFoundException(msg);
+ }
+ // See if we already have something in the changed nodes ...
+ UUID uuid = childNodeInfo.getUuid();
+ NodeInfo childInfo = changedNodes.get(uuid);
+ if (childInfo == null) {
+ // Or in the cache ...
+ childInfo = cachedNodes.get(uuid);
+ if (childInfo == null) {
+ // At this point, we've reached the bottom of the nodes that we have locally.
+ // Get the actual location of the last 'info', since all paths will be relative to it...
+ Location actualLocation = info.getOriginalLocation();
+ Path actualPath = actualLocation.getPath();
+ Path nextPath = pathFactory.create(actualPath, child);
+ if (pathsIter.hasNext()) {
+ // There are multiple remaining paths, so load them all in one batch operation,
+ // starting at the top-most path (the one we're currently at)...
+ List<Path> pathsInBatch = new LinkedList<Path>();
+ Results batchResults = null;
+ try {
+ Graph.Batch batch = store.batch();
+ batch.read(nextPath);
+ pathsInBatch.add(nextPath);
+ while (pathsIter.hasNext()) {
+ child = pathsIter.next();
+ nextPath = pathFactory.create(nextPath, child);
+ batch.read(nextPath);
+ pathsInBatch.add(nextPath);
+ }
+ batchResults = batch.execute();
+ } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+ Path fromPath = getPathFor(fromInfo);
+ throw new PathNotFoundException(JcrI18n.pathNotFoundRelativeTo.text(relativePath,
+ fromPath,
+ workspaceName));
+ } catch (RepositorySourceException e) {
+ throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithUuid.text(uuid,
+ workspaceName,
+ e.getLocalizedMessage()));
+ }
+ // Now process all of the nodes that we loaded, again starting at the top and going down ...
+ for (Path batchPath : pathsInBatch) {
+ org.jboss.dna.graph.Node dnaNode = batchResults.getNode(batchPath);
+ childInfo = createNodeInfoFrom(dnaNode, info);
+ this.cachedNodes.put(childInfo.getUuid(), childInfo);
+ info = childInfo;
+ }
+ } else {
+ // This is the last path, so do it a little more efficiently than above ...
+ childInfo = loadFromGraph(nextPath, info);
+ info = childInfo;
+ }
+ } else {
+ info = childInfo;
+ }
+ } else {
+ info = childInfo;
+ }
+ }
+ }
+ return info;
+ }
+
+ /**
+ * Find the property information with the given identifier. If the property is not yet loaded into the cache, the node (and
+ * its properties) will be read from the repository.
+ *
+ * @param propertyId the identifier for the property; may not be null
+ * @return the property information, or null if the node does not contain the specified property
+ * @throws PathNotFoundException if the node containing this property does not exist
+ * @throws RepositoryException if there is an error while obtaining the information
+ */
+ PropertyInfo findPropertyInfo( PropertyId propertyId ) throws PathNotFoundException, RepositoryException {
+ NodeInfo info = findNodeInfo(propertyId.getNodeId());
+ return info.getProperty(propertyId.getPropertyName());
+ }
+
+ Path getPathFor( UUID uuid ) throws ItemNotFoundException, RepositoryException {
+ if (uuid == root) return rootPath;
+ return getPathFor(findNodeInfo(uuid));
+ }
+
+ Path getPathFor( NodeInfo info ) throws ItemNotFoundException, RepositoryException {
+ if (info != null && info.getUuid() == root) return rootPath;
+ LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+ while (info != null) {
+ UUID parent = info.getParent();
+ if (parent == null) break;
+ NodeInfo parentInfo = findNodeInfo(parent);
+ ChildNode child = parentInfo.getChildren().getChild(info.getUuid());
+ if (child == null) break;
+ Path.Segment segment = pathFactory.createSegment(child.getName(), child.getSnsIndex());
+
+ segments.addFirst(segment);
+ info = parentInfo;
+ }
+ return pathFactory.createAbsolutePath(segments);
+ }
+
+ Path getPathFor( PropertyInfo propertyInfo ) throws ItemNotFoundException, RepositoryException {
+ Path nodePath = getPathFor(propertyInfo.getNodeUuid());
+ return pathFactory.create(nodePath, propertyInfo.getPropertyName());
+ }
+
+ Path getPathFor( PropertyId propertyId ) throws ItemNotFoundException, RepositoryException {
+ return getPathFor(findPropertyInfo(propertyId));
+ }
+
+ protected Name getNameOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+ findNodeInfoForRoot();
+ if (nodeUuid == root) return nameFactory.create("");
+ // Get the parent ...
+ NodeInfo info = findNodeInfo(nodeUuid);
+ NodeInfo parent = findNodeInfo(info.getParent());
+ ChildNode child = parent.getChildren().getChild(info.getUuid());
+ return child.getName();
+ }
+
+ protected int getSnsIndexOf( UUID nodeUuid ) throws ItemNotFoundException, RepositoryException {
+ findNodeInfoForRoot();
+ if (nodeUuid == root) return 1;
+ // Get the parent ...
+ NodeInfo info = findNodeInfo(nodeUuid);
+ NodeInfo parent = findNodeInfo(info.getParent());
+ ChildNode child = parent.getChildren().getChild(info.getUuid());
+ return child.getSnsIndex();
+ }
+
+ /**
+ * Load from the underlying repository graph the information for the node with the supplied UUID. This method returns the
+ * information for the requested node (after placing it in the cache), but this method may (at its discretion) also load and
+ * cache information for other nodes.
+ * <p>
+ * Note that this method does not check the cache before loading from the repository graph.
+ * </p>
+ *
+ * @param uuid the UUID of the node; may not be null
+ * @param parentInfo the parent information; may be null if not known
+ * @return the information for the node
+ * @throws ItemNotFoundException if the node does not exist in the repository
+ * @throws RepositoryException if there was an error obtaining this information from the repository
+ */
+ protected NodeInfo loadFromGraph( UUID uuid,
+ NodeInfo parentInfo ) throws ItemNotFoundException, RepositoryException {
+ // Load the node information from the store ...
+ try {
+ org.jboss.dna.graph.Node node = store.getNodeAt(uuid);
+ NodeInfo info = createNodeInfoFrom(node, parentInfo);
+ this.cachedNodes.put(info.getUuid(), info);
+ return info;
+ } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+ throw new ItemNotFoundException(JcrI18n.itemNotFoundWithUuid.text(uuid, workspaceName, e.getLocalizedMessage()));
+ } catch (RepositorySourceException e) {
+ throw new RepositoryException(
+ JcrI18n.errorWhileFindingNodeWithUuid.text(uuid, workspaceName, e.getLocalizedMessage()));
+ }
+ }
+
+ /**
+ * Load from the underlying repository graph the information for the node with the supplied UUID. This method returns the
+ * information for the requested node (after placing it in the cache), but this method may (at its discretion) also load and
+ * cache information for other nodes.
+ * <p>
+ * Note that this method does not check the cache before loading from the repository graph.
+ * </p>
+ *
+ * @param path the path to the node; may not be null
+ * @param parentInfo the parent information; may be null if not known
+ * @return the information for the node
+ * @throws PathNotFoundException if the node does not exist in the repository
+ * @throws RepositoryException if there was an error obtaining this information from the repository
+ */
+ protected NodeInfo loadFromGraph( Path path,
+ NodeInfo parentInfo ) throws PathNotFoundException, RepositoryException {
+ // Load the node information from the store ...
+ try {
+ org.jboss.dna.graph.Node node = store.getNodeAt(path);
+ NodeInfo info = createNodeInfoFrom(node, parentInfo);
+ this.cachedNodes.put(info.getUuid(), info);
+ return info;
+ } catch (org.jboss.dna.graph.property.PathNotFoundException e) {
+ throw new PathNotFoundException(JcrI18n.pathNotFound.text(path, workspaceName));
+ } catch (RepositorySourceException e) {
+ throw new RepositoryException(JcrI18n.errorWhileFindingNodeWithPath.text(path, workspaceName));
+ }
+ }
+
+ /**
+ * Create the {@link NodeInfo} object given the DNA graph node and the parent node information (if it is available).
+ *
+ * @param graphNode the DNA graph node; may not be null
+ * @param parentInfo the information for the parent node, or null if the supplied graph node represents the root node, or if
+ * the parent information is not known
+ * @return the node information; never null
+ * @throws RepositoryException if there is an error determining the child {@link NodeDefinition} for the supplied node,
+ * preventing the node information from being constructed
+ */
+ private NodeInfo createNodeInfoFrom( org.jboss.dna.graph.Node graphNode,
+ NodeInfo parentInfo ) throws RepositoryException {
+ // Now get the DNA node's UUID and find the DNA property containing the UUID ...
+ Location location = graphNode.getLocation();
+ ValueFactories factories = context.getValueFactories();
+ UUID uuid = location.getUuid();
+ org.jboss.dna.graph.property.Property uuidProperty = null;
+ if (uuid != null) {
+ // Check for an identification property ...
+ uuidProperty = location.getIdProperty(JcrLexicon.UUID);
+ if (uuidProperty == null) uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+ }
+ if (uuidProperty == null) {
+ uuidProperty = graphNode.getProperty(JcrLexicon.UUID);
+ if (uuidProperty != null) {
+ // Grab the first 'good' UUID value ...
+ for (Object uuidValue : uuidProperty) {
+ try {
+ uuid = factories.getUuidFactory().create(uuidValue);
+ break;
+ } catch (ValueFormatException e) {
+ // Ignore; just continue with the next property value
+ }
+ }
+ }
+ if (uuid == null) {
+ // Look for the DNA UUID property ...
+ org.jboss.dna.graph.property.Property dnaUuidProperty = graphNode.getProperty(DnaLexicon.UUID);
+ if (dnaUuidProperty != null) {
+ // Grab the first 'good' UUID value ...
+ for (Object uuidValue : dnaUuidProperty) {
+ try {
+ uuid = factories.getUuidFactory().create(uuidValue);
+ break;
+ } catch (ValueFormatException e) {
+ // Ignore; just continue with the next property value
+ }
+ }
+ }
+ }
+ }
+ if (uuid == null) uuid = UUID.randomUUID();
+ if (uuidProperty == null) uuidProperty = propertyFactory.create(JcrLexicon.UUID, uuid);
+
+ // Either the UUID is not known, or there was no node. Either way, we have to create the node ...
+ if (uuid == null) uuid = UUID.randomUUID();
+
+ // Look for the primary type of the node ...
+ Map<Name, Property> graphProperties = graphNode.getPropertiesByName();
+ final boolean isRoot = location.getPath().isRoot();
+ Name primaryTypeName = null;
+ org.jboss.dna.graph.property.Property primaryTypeProperty = graphNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+ if (primaryTypeProperty != null && !primaryTypeProperty.isEmpty()) {
+ try {
+ primaryTypeName = factories.getNameFactory().create(primaryTypeProperty.getFirstValue());
+ } catch (ValueFormatException e) {
+ // use the default ...
+ }
+ }
+ if (primaryTypeName == null) {
+ // We have to have a primary type, so use the default ...
+ if (isRoot) {
+ primaryTypeName = DnaLexicon.ROOT;
+ primaryTypeProperty = propertyFactory.create(JcrLexicon.PRIMARY_TYPE, primaryTypeName);
+ } else {
+ primaryTypeName = defaultPrimaryTypeName;
+ primaryTypeProperty = defaultPrimaryTypeProperty;
+ }
+ // We have to add this property to the graph node...
+ graphProperties = new HashMap<Name, Property>(graphProperties);
+ graphProperties.put(primaryTypeProperty.getName(), primaryTypeProperty);
+ }
+ assert primaryTypeProperty != null;
+ assert primaryTypeProperty.isEmpty() == false;
+
+ // Look for a node definition stored on the node ...
+ JcrNodeDefinition definition = null;
+ org.jboss.dna.graph.property.Property nodeDefnProperty = graphProperties.get(DnaLexicon.NODE_DEFINITON);
+ if (nodeDefnProperty != null && !nodeDefnProperty.isEmpty()) {
+ String nodeDefinitionString = stringFactory.create(nodeDefnProperty.getFirstValue());
+ NodeDefinitionId id = NodeDefinitionId.fromString(nodeDefinitionString, nameFactory);
+ definition = nodeTypes().getNodeDefinition(id);
+ }
+ // Figure out the node definition for this node ...
+ if (definition == null) {
+ if (isRoot) {
+ definition = nodeTypes().getRootNodeDefinition();
+ } else {
+ // We need the parent ...
+ Path path = location.getPath();
+ if (parentInfo == null) {
+ Path parentPath = path.getParent();
+ parentInfo = findNodeInfo(null, parentPath.getNormalizedPath());
+ }
+ Name childName = path.getLastSegment().getName();
+ definition = findNodeDefinitionForChild(parentInfo, childName, primaryTypeName);
+ if (definition == null) {
+ String msg = JcrI18n.nodeDefinitionCouldNotBeDeterminedForNode.text(path, workspaceName);
+ throw new RepositorySourceException(msg);
+ }
+ }
+ }
+
+ // Create the node information ...
+ NodeInfo info = new NodeInfo(location, primaryTypeName, definition.getId());
+ if (parentInfo != null) {
+ info.setParent(parentInfo.getUuid());
+ }
+
+ // --------------------------------------------------
+ // Set the node's children ...
+ // --------------------------------------------------
+ info.setChildren(graphNode.getChildren());
+
+ // ------------------------------------------------------
+ // Set the node's properties ...
+ // ------------------------------------------------------
+ // First get the property type for each property, based upon the primary type and mixins ...
+ // The map with single-valued properties...
+ Map<Name, PropertyDefinition> svPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+ // ... and the map with multi-valued properties
+ Map<Name, PropertyDefinition> mvPropertyDefinitionsByPropertyName = new HashMap<Name, PropertyDefinition>();
+
+ boolean referenceable = false;
+
+ List<PropertyDefinition> anyPropertyDefinitions = new LinkedList<PropertyDefinition>();
+ // Start with the primary type ...
+ NodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+ for (PropertyDefinition propertyDefn : primaryType.getPropertyDefinitions()) {
+ String nameString = propertyDefn.getName();
+ if ("*".equals(nameString)) {
+ anyPropertyDefinitions.add(propertyDefn);
+ continue;
+ }
+ Name name = nameFactory.create(nameString);
+
+ if (propertyDefn.isMultiple()) {
+ PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+ if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+ } else {
+ PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+ if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+ }
+ }
+ // The process the mixin types ...
+ org.jboss.dna.graph.property.Property mixinTypesProperty = graphProperties.get(JcrLexicon.MIXIN_TYPES);
+ if (mixinTypesProperty != null && !mixinTypesProperty.isEmpty()) {
+ for (Object mixinTypeValue : mixinTypesProperty) {
+ Name mixinTypeName = nameFactory.create(mixinTypeValue);
+ if (!referenceable && JcrMixLexicon.REFERENCEABLE.equals(mixinTypeName)) referenceable = true;
+ String mixinTypeNameString = mixinTypeName.getString(namespaces);
+ NodeType mixinType = nodeTypes().getNodeType(mixinTypeNameString);
+ for (PropertyDefinition propertyDefn : mixinType.getPropertyDefinitions()) {
+ String nameString = propertyDefn.getName();
+ if ("*".equals(nameString)) {
+ anyPropertyDefinitions.add(propertyDefn);
+ continue;
+ }
+ Name name = nameFactory.create(nameString);
+ if (propertyDefn.isMultiple()) {
+ PropertyDefinition prev = mvPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+ if (prev != null) mvPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+ } else {
+ PropertyDefinition prev = svPropertyDefinitionsByPropertyName.put(name, propertyDefn);
+ if (prev != null) svPropertyDefinitionsByPropertyName.put(name, prev); // put the first one back ...
+ }
+ }
+ }
+ }
+
+ // Now create the JCR property object wrappers around the other properties ...
+ for (org.jboss.dna.graph.property.Property dnaProp : graphProperties.values()) {
+ Name name = dnaProp.getName();
+
+ // Figure out the JCR property type for this property ...
+ PropertyDefinition propertyDefinition;
+ if (dnaProp.isMultiple()) {
+ propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+ } else {
+ propertyDefinition = svPropertyDefinitionsByPropertyName.get(name);
+
+ // If the property has only one value, dnaProp.isMultiple() will return false, but the
+ // property may actually be a multi-valued property that happens to have one property set.
+ if (propertyDefinition == null) {
+ propertyDefinition = mvPropertyDefinitionsByPropertyName.get(name);
+ }
+ }
+
+ // If no property type was found for this property, see if there is a wildcard property ...
+ if (propertyDefinition == null) {
+ for (Iterator<PropertyDefinition> iter = anyPropertyDefinitions.iterator(); iter.hasNext();) {
+ PropertyDefinition nextDef = iter.next();
+
+ // Grab the first residual definition that matches on cardinality (single-valued vs. multi-valued)
+ if ((nextDef.isMultiple() && dnaProp.isMultiple()) || (!nextDef.isMultiple() && !dnaProp.isMultiple())) {
+ propertyDefinition = nextDef;
+ break;
+ }
+ }
+ }
+
+ // If there still is no property type defined ...
+ if (propertyDefinition == null) {
+ assert anyPropertyDefinitions.isEmpty();
+ if (INCLUDE_PROPERTIES_NOT_ALLOWED_BY_NODE_TYPE_OR_MIXINS) {
+ // We can use the "nt:unstructured" property definitions for any property ...
+ NodeType unstructured = nodeTypes().getNodeType(JcrNtLexicon.UNSTRUCTURED);
+ for (PropertyDefinition anyDefinition : unstructured.getDeclaredPropertyDefinitions()) {
+ if (anyDefinition.isMultiple()) {
+ propertyDefinition = anyDefinition;
+ break;
+ }
+ }
+ }
+ }
+ if (propertyDefinition == null) {
+ // We're supposed to skip this property (since we don't have a definition for it) ...
+ continue;
+ }
+
+ // Figure out if this is a multi-valued property ...
+ boolean isMultiple = propertyDefinition.isMultiple();
+ if (!isMultiple && dnaProp.isEmpty()) {
+ // Only multi-valued properties can have no values; so if not multi-valued, then skip ...
+ continue;
+ }
+
+ // Figure out the property type ...
+ int propertyType = propertyDefinition.getRequiredType();
+ if (propertyType == PropertyType.UNDEFINED) {
+ propertyType = JcrSession.jcrPropertyTypeFor(dnaProp);
+ }
+
+ // Record the property in the node information ...
+ PropertyId propId = new PropertyId(uuid, name);
+ info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, propertyType, dnaProp);
+ }
+
+ // Now add the "jcr:uuid" property if and only if referenceable ...
+ if (referenceable) {
+ // We know that this property is single-valued
+ PropertyDefinition propertyDefinition = svPropertyDefinitionsByPropertyName.get(JcrLexicon.UUID);
+ PropertyId propId = new PropertyId(uuid, JcrLexicon.UUID);
+ info.setProperty(propId, (JcrPropertyDefinition)propertyDefinition, PropertyType.STRING, uuidProperty);
+ } else {
+ // Make sure there is NOT a "jcr:uuid" property ...
+ info.removeProperty(JcrLexicon.UUID);
+ }
+ // Make sure the "dna:uuid" property did not get in there ...
+ info.removeProperty(DnaLexicon.UUID);
+
+ return info;
+ }
+
+ /**
+ * Utility method to find the {@link NodeDefinition} for a child node with the supplied {@link Name name}, parent information,
+ * and the primary node type of the child.
+ *
+ * @param parentInfo the parent information; may not be null
+ * @param childName the name of the child node (without any same-name-sibling index); may not be null
+ * @param primaryTypeOfChild the name of the child's primary type
+ * @return the node definition for this child, as best as can be determined, or null if the node definition could not be
+ * determined
+ * @throws RepositoryException if the parent's pimary node type cannot be found in the {@link NodeTypeManager}
+ */
+ protected JcrNodeDefinition findNodeDefinitionForChild( NodeInfo parentInfo,
+ Name childName,
+ Name primaryTypeOfChild ) throws RepositoryException {
+ // Get the primary type of the parent, and look at it's child definitions ...
+ Name primaryTypeName = parentInfo.getPrimaryTypeName();
+ JcrNodeType primaryType = nodeTypes().getNodeType(primaryTypeName);
+ if (primaryType == null) {
+ String msg = JcrI18n.missingNodeTypeForExistingNode.text(primaryTypeName, parentInfo.getUuid(), workspaceName);
+ throw new RepositoryException(msg);
+ }
+ // TODO: should this also check the mixins?
+ return primaryType.findBestNodeDefinitionForChild(childName, primaryTypeOfChild);
+ }
+
+ /**
+ * The information that describes a node. This is the information that is kept in the {@link SessionCache#cachedNodes cache}
+ * and in the record of {@link SessionCache#changedNodes changes} made by the session but not yet commited/saved.
+ */
+ @NotThreadSafe
+ protected class NodeInfo {
+ private final Location originalLocation;
+ private final UUID uuid;
+ private UUID parent;
+ private final Name primaryTypeName;
+ private final NodeDefinitionId definition;
+ private final Children children;
+ private final Map<Name, PropertyInfo> properties;
+
+ protected NodeInfo( Location originalLocation,
+ Name primaryTypeName,
+ NodeDefinitionId definition ) {
+ this.originalLocation = originalLocation;
+ this.primaryTypeName = primaryTypeName;
+ this.definition = definition;
+ this.uuid = this.originalLocation.getUuid();
+ this.children = new Children(this.uuid);
+ this.properties = new HashMap<Name, PropertyInfo>();
+ assert this.uuid != null;
+ assert this.definition != null;
+ assert this.primaryTypeName != null;
+ }
+
+ /**
+ * @return location
+ */
+ public Location getOriginalLocation() {
+ return originalLocation;
+ }
+
+ /**
+ * @return uuid
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * @return parent
+ */
+ public UUID getParent() {
+ return parent;
+ }
+
+ /**
+ * @param parent Sets parent to the specified value.
+ */
+ protected void setParent( UUID parent ) {
+ this.parent = parent;
+ }
+
+ /**
+ * @return primaryTypeName
+ */
+ public Name getPrimaryTypeName() {
+ return primaryTypeName;
+ }
+
+ /**
+ * @return definition
+ */
+ public NodeDefinitionId getDefinitionId() {
+ return definition;
+ }
+
+ /**
+ * Get the children for this node.
+ *
+ * @return the children; never null but possibly empty
+ */
+ public Children getChildren() {
+ return children;
+ }
+
+ /**
+ * @param children Sets children to the specified value.
+ * @return the children information; never null
+ */
+ public Children setChildren( List<Location> children ) {
+ this.children.append(SessionCache.this, children, true);
+ return this.children;
+ }
+
+ /**
+ * Get the map of information for each property.
+ *
+ * @return the information for each property; never null but possibly (though unlikely) empty
+ */
+ public Map<Name, PropertyInfo> getProperties() {
+ return this.properties; // never null
+ }
+
+ public PropertyInfo getProperty( Name name ) {
+ return this.properties.get(name);
+ }
+
+ public PropertyInfo setProperty( PropertyId id,
+ JcrPropertyDefinition definition,
+ int propertyType,
+ Property dnaProperty ) {
+ // Initialize the map if required (this never replaces it, though) ...
+ PropertyInfo info = new PropertyInfo(id, definition.getId(), propertyType, dnaProperty, definition.isMultiple());
+ return this.properties.put(id.getPropertyName(), info);
+ }
+
+ public PropertyInfo removeProperty( Name name ) {
+ return this.properties.remove(name);
+ }
+ }
+
+ /**
+ * An immutable representation of the name and current value(s) for a property, along with the JCR metadata for the property,
+ * including the {@link PropertyInfo#getDefinitionId() property definition} and {@link PropertyInfo#getPropertyType() property
+ * type}.
+ * <p>
+ * This class is immutable, which means that clients should never hold onto an instance. Instead, clients can obtain an
+ * instance by using a {@link PropertyId}, quickly use the information in the instance, and then immediately discard their
+ * reference. This is because these instances are replaced and discarded whenever anything about the property changes.
+ * </p>
+ */
+ @Immutable
+ public static class PropertyInfo {
+ private final PropertyId propertyId;
+ private final PropertyDefinitionId definitionId;
+ private final Property dnaProperty;
+ private final int propertyType;
+ private final boolean multiValued;
+
+ protected PropertyInfo( PropertyId propertyId,
+ PropertyDefinitionId definitionId,
+ int propertyType,
+ Property dnaProperty,
+ boolean multiValued ) {
+ this.propertyId = propertyId;
+ this.definitionId = definitionId;
+ this.propertyType = propertyType;
+ this.dnaProperty = dnaProperty;
+ this.multiValued = multiValued;
+ }
+
+ /**
+ * Get the durable identifier for this property.
+ *
+ * @return propertyId
+ */
+ public PropertyId getPropertyId() {
+ return propertyId;
+ }
+
+ /**
+ * Get the UUID of the node to which this property belongs.
+ *
+ * @return the owner node's UUID; never null
+ */
+ public UUID getNodeUuid() {
+ return propertyId.getNodeId();
+ }
+
+ /**
+ * The identifier for the property definition.
+ *
+ * @return the property definition ID; never null
+ */
+ public PropertyDefinitionId getDefinitionId() {
+ return definitionId;
+ }
+
+ /**
+ * Get the DNA Property, which contains the name and value(s)
+ *
+ * @return the property; never null
+ */
+ public Property getProperty() {
+ return dnaProperty;
+ }
+
+ /**
+ * Get the property name.
+ *
+ * @return the property name; never null
+ */
+ public Name getPropertyName() {
+ return dnaProperty.getName();
+ }
+
+ /**
+ * Get the JCR {@link PropertyType} for this property.
+ *
+ * @return the property type
+ */
+ public int getPropertyType() {
+ return propertyType;
+ }
+
+ /**
+ * @return multiValued
+ */
+ public boolean isMultiValued() {
+ return multiValued;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return propertyId.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertyInfo) {
+ return propertyId.equals(((PropertyInfo)obj).getPropertyId());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(propertyId);
+ sb.append(" defined by ").append(definitionId);
+ sb.append(" of type ").append(PropertyType.nameFromValue(propertyType));
+ if (dnaProperty.isSingle()) {
+ sb.append(" with value ");
+ } else {
+ sb.append(" with values ");
+ }
+ sb.append(dnaProperty.getValuesAsArray());
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Class that maintains the ordered list of {@link ChildNode} instances. This class uses a {@link ListMultimap} to maintain
+ * insertion order of the child nodes, and to allow fast access to the children with a specified name.
+ */
+ @ThreadSafe
+ public final static class Children implements Iterable<ChildNode> {
+ private final UUID parentUuid;
+ private final Map<UUID, ChildNode> childrenByUuid;
+ private final ListMultimap<Name, ChildNode> childrenByName;
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ Children( UUID parentUuid ) {
+ this.parentUuid = parentUuid;
+ this.childrenByUuid = new HashMap<UUID, ChildNode>();
+ this.childrenByName = new LinkedListMultimap<Name, ChildNode>();
+ }
+
+ /**
+ * Get the number of children.
+ *
+ * @return the number of children
+ */
+ public int size() {
+ try {
+ lock.readLock().lock();
+ return childrenByName.size();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the children.
+ *
+ * @return a copy of the list of children
+ */
+ public List<ChildNode> asList() {
+ try {
+ lock.readLock().lock();
+ return new ArrayList<ChildNode>(childrenByName.values());
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the children.
+ *
+ * @return a copy of the list of children
+ */
+ public Iterator<ChildNode> iterator() {
+ return asList().iterator();
+ }
+
+ /**
+ * The UUID of the parent node.
+ *
+ * @return the parent node's UUID
+ */
+ public UUID getParentUuid() {
+ return parentUuid;
+ }
+
+ /**
+ * Get the child with the given UUID.
+ *
+ * @param uuid the UUID of the child node
+ * @return the child node, or null if there is no child with the supplied UUID
+ */
+ public ChildNode getChild( UUID uuid ) {
+ try {
+ lock.readLock().lock();
+ return this.childrenByUuid.get(uuid);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the child given the path segment.
+ *
+ * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+ * {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+ * @return the information for the child node, or null if no such child existed
+ */
+ public ChildNode getChild( Path.Segment segment ) {
+ try {
+ lock.readLock().lock();
+ List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+ int snsIndex = segment.getIndex();
+ if (childrenWithName.size() < snsIndex) return null;
+ return childrenWithName.get(snsIndex - 1);
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the same-name-sibling children that all share the supplied name.
+ *
+ * @param name the name for the children; may not be null
+ * @return the children with the supplied name; never null
+ */
+ public List<ChildNode> getChildren( Name name ) {
+ try {
+ lock.readLock().lock();
+ return new ArrayList<ChildNode>(this.childrenByName.get(name));
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Remove the child with the given path segment.
+ *
+ * @param segment the path segment for the child, which includes the {@link Path.Segment#getName() name} and
+ * {@link Path.Segment#getIndex() one-based same-name-sibling index}; may not be null
+ * @return the information for the child node that was removed, or null if no such child existed
+ */
+ public ChildNode remove( Path.Segment segment ) {
+ try {
+ lock.writeLock().lock();
+ List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName());
+ int snsIndex = segment.getIndex();
+ int numChildrenWithName = childrenWithName.size();
+ if (numChildrenWithName < snsIndex) return null;
+ ChildNode result = childrenWithName.remove(snsIndex);
+ this.childrenByUuid.remove(result.getUuid());
+ --numChildrenWithName;
+ if (numChildrenWithName > snsIndex) {
+ // We need to reduce the SNS index of every child after the one that was just removed ...
+ ListIterator<ChildNode> siblingIter = childrenWithName.listIterator(snsIndex);
+ while (siblingIter.hasNext()) {
+ // Remove the next child and replace with one having the correct SNS index ...
+ ChildNode next = siblingIter.next();
+ siblingIter.remove();
+ siblingIter.set(next.withChangedSnsIndex(-1));
+ }
+ }
+ return result;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Add a new child to the end of the list of existing children a new child with the supplied name and UUID. The
+ * same-name-sibling will be determined to be one more than the number of existing children with the same name.
+ *
+ * @param cache the cache; may not be null
+ * @param name the name for the child to be appended; may not be null
+ * @param childUuid the UUID of the child; may not be null
+ * @return the information for the newly-added child; never null
+ */
+ public ChildNode append( SessionCache cache,
+ Name name,
+ UUID childUuid ) {
+ try {
+ lock.writeLock().lock();
+ List<ChildNode> childrenWithName = this.childrenByName.get(name);
+ ChildNode child = new ChildNode(childUuid, name, childrenWithName.size() + 1);
+ childrenWithName.add(child);
+ this.childrenByUuid.put(childUuid, child);
+ // Look for the child in the cache/changed nodes ...
+ NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
+ if (childInfo != null) childInfo.setParent(parentUuid);
+ return child;
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Append the children described by the supplied Location objects, optionally removing all existing children first.
+ *
+ * @param cache the cache; may not be null
+ * @param children a list containing a Location object for each child
+ * @param removeExistingFirst true if the existing children should be removed before these children are added, or false if
+ * these children should be appended to the existing children
+ */
+ public void append( SessionCache cache,
+ List<Location> children,
+ boolean removeExistingFirst ) {
+ try {
+ lock.writeLock().lock();
+ if (removeExistingFirst && !this.childrenByName.isEmpty()) {
+ for (ChildNode child : this.childrenByName.values()) {
+ // Look for the child in the cache/changed nodes ...
+ NodeInfo childInfo = cache.findNodeInfoInCache(child.getUuid());
+ if (childInfo != null) childInfo.setParent(null);
+ // TODO: These nodes are deleted and should be handled as deletes ...
+ }
+ this.childrenByUuid.clear();
+ this.childrenByName.clear();
+ }
+ for (Location childLocation : children) {
+ UUID childUuid = childLocation.getUuid();
+ Path.Segment segment = childLocation.getPath().getLastSegment();
+ Name name = segment.getName();
+ ChildNode child = new ChildNode(childUuid, name, segment.getIndex());
+ this.childrenByName.put(name, child);
+ this.childrenByUuid.put(childUuid, child);
+ // Look for the child in the cache/changed nodes ...
+ NodeInfo childInfo = cache.findNodeInfoInCache(childUuid);
+ if (childInfo != null) childInfo.setParent(parentUuid);
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ try {
+ lock.readLock().lock();
+ boolean first = true;
+ for (ChildNode child : childrenByName.values()) {
+ if (!first) sb.append(", ");
+ else first = false;
+ sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']');
+ }
+ } finally {
+ lock.readLock().unlock();
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * The information about a child node. This is designed to be found in the {@link Children}, used quickly, and discarded.
+ * Clients should not hold on to these objects, since any changes to the children involve discarding the old ChildNode objects
+ * and replacing them with new instances.
+ */
+ @Immutable
+ public final static class ChildNode {
+ private final UUID uuid;
+ private final Name name;
+ private final int snsIndex;
+
+ protected ChildNode( UUID uuid,
+ Name name,
+ int snsIndex ) {
+ this.uuid = uuid;
+ this.name = name;
+ this.snsIndex = snsIndex;
+ assert this.snsIndex > 0;
+ }
+
+ /**
+ * Get the UUID of the node.
+ *
+ * @return the node's UUID; never null
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * Get the name of the node.
+ *
+ * @return the node's current name; never null
+ */
+ public Name getName() {
+ return name;
+ }
+
+ /**
+ * Get the same-name-sibling index of the node.
+ *
+ * @return the node's SNS index; always positive
+ */
+ public int getSnsIndex() {
+ return snsIndex;
+ }
+
+ /**
+ * Return a new child node that has a changed SNS index.
+ *
+ * @param delta the amount the change the SNS index, either positive to increase the value or negative to decrease the
+ * value
+ * @return the copy of this, with a changed SNS index; never null
+ */
+ public ChildNode withChangedSnsIndex( int delta ) {
+ return new ChildNode(uuid, name, snsIndex + delta);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return uuid.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildNode) {
+ return this.uuid.equals(((ChildNode)obj).uuid);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return uuid.toString() + " ( " + name + "[" + snsIndex + "] )";
+ }
+ }
+
+}
Property changes on: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/SessionCache.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties
===================================================================
--- trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/main/resources/org/jboss/dna/jcr/JcrI18n.properties 2009-03-16 22:50:09 UTC (rev 777)
@@ -28,7 +28,8 @@
defaultWorkspaceName=
inputStreamConsumed = This value was already consumed as an input stream.
nonInputStreamConsumed = This value was already consumed as a non-input stream.
-pathNotFound = No item exists at path {0}
+pathNotFound = No item exists at path {0} in workspace "{1}"
+pathNotFoundRelativeTo = No item exists at path {0} relative to {1} in workspace "{2}"
permissionDenied = Permission denied to perform actions "{1}" on path {0}.
repositoryMustBeConfigured = DNA repositories must be configured with either a repository source factory or a repository source.
sourceInUse = All sessions must end before a new repository source can be set.
@@ -51,9 +52,19 @@
invalidRelativePath = "{0}" is not a valid relative path
invalidPathParameter = The "{1}" parameter value "{0}" was not a valid path
invalidNamePattern = The "{1}" name pattern contained the '{0}' character, which is not allowed in a name pattern
-itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}"
-errorWhileFindingNodeWithUuid = Error while finding the item with UUID "{0}" in workspace "{1}"
+noPrimaryItemNameDefinedOnPrimaryType = The primary type "{0}" for node "{1}" in workspace "{2}" does not define a primary item name
+primaryItemNameForPrimaryTypeIsNotValid = The primary type "{0}" for node "{2}" in workspace "{3}" defines an invalid primary item name ("{1}")
+primaryItemDoesNotExist = The node "{2}" in workspace "{3}" does not have an item named "{1}" as defined by its primary type "{0}"
+itemNotFoundWithUuid = An item with UUID "{0}" could not be found in workspace "{1}": {2}
+itemNotFoundAtPath = An item at "{0}" could not be found in workspace "{1}"
+itemNotFoundAtPathRelativeToReferenceNode = An item at "{0}" relative to "{1}" could not be found in workspace "{2}"
+propertyNotFoundAtPathRelativeToReferenceNode = A property at "{0}" relative to "{1}" could not be found in workspace "{2}"
+nodeNotFoundAtPathRelativeToReferenceNode = A node at "{0}" relative to "{1}" could not be found in workspace "{2}"
+childNotFoundUnderNode = The child "{0}" could not be found under "{1}" in workspace "{2}"
+errorWhileFindingNodeWithUuid = Error while finding the node with UUID "{0}" in workspace "{1}": {2}
+errorWhileFindingNodeWithPath = Error while finding the node "{0}" in workspace "{1}"
nodeDefinitionCouldNotBeDeterminedForNode = Unable to determine a valid node definition for the node "{0}" in workspace "{1}"
+missingNodeTypeForExistingNode = Missing primary node type "{0}" for node {1} in workspace "{2}"
REP_NAME_DESC = DNA Repository
REP_VENDOR_DESC = JBoss - A division of Red Hat Middleware LLC
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrItemTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -27,24 +27,36 @@
import static org.junit.Assert.assertThat;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
-import javax.jcr.Session;
+import org.jboss.dna.graph.property.Path;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
-/**
- * @author jverhaeg
- */
public class AbstractJcrItemTest {
private AbstractJcrItem item;
+ @Mock
+ private SessionCache cache;
@Before
public void before() {
- item = new AbstractJcrItem() {
+ MockitoAnnotations.initMocks(this);
+ item = new AbstractJcrItem(cache) {
public void accept( ItemVisitor visitor ) {
}
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrItem#path()
+ */
+ @Override
+ Path path() {
+ throw new UnsupportedOperationException();
+ }
+
public String getName() {
return null;
}
@@ -57,10 +69,6 @@
return null;
}
- public Session getSession() {
- return null;
- }
-
public boolean isNode() {
return false;
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrNodeTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -25,13 +25,13 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import java.io.InputStream;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -39,20 +39,23 @@
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
+import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.version.Version;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@@ -64,36 +67,16 @@
*/
public class AbstractJcrNodeTest {
- static MockAbstractJcrNode createChild( JcrSession session,
- String name,
- int index,
- List<Location> children,
- AbstractJcrNode parent ) throws Exception {
- MockAbstractJcrNode child = new MockAbstractJcrNode(session, name, parent);
- String parentPath = parent.getPath();
- String childPath = parentPath + "/" + name + "[" + index + "]";
- Path path = session.getExecutionContext().getValueFactories().getPathFactory().create(childPath);
- Location location = Location.create(path, UUID.randomUUID());
- children.add(location);
- // Stub the session to return this node ...
- String absolutePath = path.getString(session.getExecutionContext().getNamespaceRegistry());
- stub(session.getChild(parent, location)).toReturn(child);
- stub(session.getNode(location.getUuid())).toReturn(child);
- stub(session.getItem(absolutePath)).toReturn(child);
- return child;
- }
-
static class MockAbstractJcrNode extends AbstractJcrNode {
- String name;
- Node parent;
+ MockAbstractJcrNode( SessionCache cache,
+ UUID uuid ) {
+ super(cache, uuid);
+ }
- MockAbstractJcrNode( JcrSession session,
- String name,
- Node parent ) {
- super(session, Location.create(UUID.randomUUID()), mock(NodeDefinition.class));
- this.name = name;
- this.parent = parent;
+ @Override
+ boolean isRoot() {
+ return false;
}
@Override
@@ -105,45 +88,67 @@
return 0;
}
- public String getName() {
- return name;
+ public String getName() throws RepositoryException {
+ return cache.getPathFor(nodeInfo()).getLastSegment().getString(namespaces());
}
- public Node getParent() {
- return parent;
+ public Node getParent() throws RepositoryException {
+ return cache.findJcrNode(nodeInfo().getParent());
}
public String getPath() throws RepositoryException {
- return (parent == null ? '/' + getName() : parent.getPath() + '/' + getName());
+ return cache.getPathFor(nodeInfo()).getString(namespaces());
}
}
+ private ExecutionContext context;
+ private UUID uuid;
private AbstractJcrNode node;
+ private Children children;
@Mock
+ private SessionCache cache;
+ @Mock
private JcrSession session;
@Mock
- private Workspace workspace;
+ private JcrNodeTypeManager nodeTypes;
@Mock
- private Repository repository;
- private List<Location> children;
- private Map<Name, Property> properties;
- private ExecutionContext context;
+ private NodeInfo info;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
context = new ExecutionContext();
- stub(session.getExecutionContext()).toReturn(context);
- children = new ArrayList<Location>();
- properties = new HashMap<Name, Property>();
- node = new MockAbstractJcrNode(session, "node", null);
- node.setProperties(properties);
+ context.getNamespaceRegistry().register("acme", "http://www.example.com");
+ uuid = UUID.randomUUID();
+ stub(cache.session()).toReturn(session);
+ stub(cache.context()).toReturn(context);
+ stub(cache.workspaceName()).toReturn("my workspace");
+ stub(cache.findNodeInfo(uuid)).toReturn(info);
+ stub(cache.getPathFor(info)).toReturn(path("/a/b/c/d"));
+ stub(session.nodeTypeManager()).toReturn(nodeTypes);
+ node = new MockAbstractJcrNode(cache, uuid);
+
+ // Create the children container for the node ...
+ children = new Children(uuid);
+ stub(info.getChildren()).toReturn(children);
}
protected Name name( String name ) {
return context.getValueFactories().getNameFactory().create(name);
}
+ protected Path relativePath( String relativePath ) {
+ return context.getValueFactories().getPathFactory().create(relativePath);
+ }
+
+ protected Path path( String absolutePath ) {
+ return context.getValueFactories().getPathFactory().create(absolutePath);
+ }
+
+ protected Value stringValueFor( Object value ) {
+ return new JcrValue(context.getValueFactories(), PropertyType.STRING, value);
+ }
+
@Test
public void shouldAllowVisitation() throws Exception {
ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -156,11 +161,6 @@
node.accept(null);
}
- @Test( expected = AssertionError.class )
- public void shouldNotAllowNoSession() throws Exception {
- new MockAbstractJcrNode(null, null, null);
- }
-
@Test( expected = ItemNotFoundException.class )
public void shouldNotAllowNegativeAncestorDepth() throws Exception {
node.getAncestor(-1);
@@ -172,20 +172,50 @@
}
@Test
- public void shouldProvideInternalUuid() throws Exception {
- UUID uuid = UUID.randomUUID();
- node.setInternalUuid(uuid);
- assertThat(node.internalUuid(), is(uuid));
+ public void shouldReturnPropertyFromGetPropertyWithValidName() throws Exception {
+ PropertyId propertyId = new PropertyId(uuid, name("test"));
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrProperty(propertyId)).toReturn(property);
+ assertThat(node.getProperty("test"), is((Property)property));
}
@Test
- public void shouldProvideNamedProperty() throws Exception {
- Property property = Mockito.mock(Property.class);
- stub(property.getName()).toReturn("test");
- properties.put(name(property.getName()), property);
- assertThat(node.getProperty("test"), is(property));
+ public void shouldReturnPropertyFromGetPropertyWithValidRelativePath() throws Exception {
+ PropertyId propertyId = new PropertyId(uuid, name("test"));
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrProperty(propertyId)).toReturn(property);
+ assertThat(node.getProperty("test"), is((Property)property));
}
+ @Test( expected = PathNotFoundException.class )
+ public void shouldFailToReturnPropertyFromGetPropertyWithNameOfPropertyThatDoesNotExist() throws Exception {
+ node.getProperty("nonExistantProperty");
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToReturnPropertyFromGetPropertyWithAbsolutePath() throws Exception {
+ node.getProperty("/test");
+ }
+
+ @Test( expected = PathNotFoundException.class )
+ public void shouldFailToReturnPropertyFromGetPropertyWithRelativePathToNonExistantItem() throws Exception {
+ node.getProperty("../bogus/path");
+ }
+
+ @Test
+ public void shouldReturnPropertyFromGetPropertyWithRelativePathToPropertyOnOtherNode() throws Exception {
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(property);
+ assertThat(node.getProperty("../good/path"), is((Property)property));
+ }
+
+ @Test( expected = PathNotFoundException.class )
+ public void shouldReturnPropertyFromGetPropertyWithRelativePathToOtherNode() throws Exception {
+ AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+ stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(otherNode);
+ node.getProperty("../good/path");
+ }
+
@Test( expected = UnsupportedOperationException.class )
public void shoudNotAllowAddMixin() throws Exception {
node.addMixin(null);
@@ -241,152 +271,210 @@
node.getLock();
}
- @Test
- public void shouldProvideNode() throws Exception {
- Node child = createChild(session, "child", 1, children, node);
- node.setChildren(children);
- stub(session.getItem("/node/child")).toReturn(child);
- assertThat(node.getNode("child"), is(child));
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowGetPropertyWithNullPath() throws Exception {
+ node.getProperty((String)null);
}
@Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowGetNodeWithNoPath() throws Exception {
- node.getNode(null);
+ public void shouldNotAllowGetPropertyWithEmptyPath() throws Exception {
+ node.getProperty("");
}
+ @Test
+ public void shouldReturnChildNodeFromGetNodeWithValidName() throws Exception {
+ AbstractJcrNode child = mock(AbstractJcrNode.class);
+ UUID childUuid = UUID.randomUUID();
+ children.append(cache, name("child"), childUuid);
+ stub(cache.findJcrNode(childUuid)).toReturn(child);
+ assertThat(node.getNode("child"), is((Node)child));
+ }
+
+ @Test
+ public void shouldReturnNonChildNodeFromGetNodeWithValidRelativePath() throws Exception {
+ AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+ stub(cache.findJcrItem(uuid, path("../other/node"))).toReturn(otherNode);
+ assertThat(node.getNode("../other/node"), is((Node)otherNode));
+ }
+
@Test( expected = PathNotFoundException.class )
- public void shouldNotProvideNodeIfPathNotFound() throws Exception {
- node.getNode("bogus");
+ public void shouldFailToReturnNodeFromGetNodeWithValidRelativePathToProperty() throws Exception {
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrItem(uuid, path("../other/node"))).toReturn(property);
+ node.getNode("../other/node");
}
@Test( expected = PathNotFoundException.class )
- public void shouldNotProvideNodeIfPathIsProperty() throws Exception {
- Property property = Mockito.mock(Property.class);
- stub(session.getItem("/property")).toReturn(property);
- node.getNode("property");
+ public void shouldFailToReturnNodeFromGetNodeWithValidRelativePathToNoNodeOrProperty() throws Exception {
+ node.getNode("../other/node");
}
@Test
- public void shouldProvideNodeIterator() throws Exception {
- assertThat(node.getNodes(), notNullValue());
+ public void shouldReturnSelfFromGetNodeWithRelativePathContainingOnlySelfReference() throws Exception {
+ assertThat(node.getNode("."), is((Node)node));
}
@Test
- public void shoudProvidePrimaryItem() throws Exception {
- Property property = Mockito.mock(Property.class);
- stub(property.getName()).toReturn("jcr:primaryItemName");
- stub(property.getString()).toReturn("primaryItem");
- properties.put(name(property.getName()), property);
- Item primaryItem = Mockito.mock(Item.class);
- stub(session.getItem("/node/primaryItem")).toReturn(primaryItem);
- assertThat(node.getPrimaryItem(), is(primaryItem));
+ public void shouldReturnParentFromGetNodeWithRelativePathContainingOnlyParentReference() throws Exception {
+ AbstractJcrNode parent = mock(AbstractJcrNode.class);
+ UUID parentUuid = UUID.randomUUID();
+ stub(info.getParent()).toReturn(parentUuid);
+ stub(cache.findJcrNode(parentUuid)).toReturn(parent);
+ assertThat(node.getNode(".."), is((Node)parent));
}
- @Test( expected = ItemNotFoundException.class )
- public void shoudNotProvidePrimaryItemIfUnavailable() throws Exception {
- node.getPrimaryItem();
+ @Test( expected = PathNotFoundException.class )
+ public void shouldFailToReturnChildNodeFromGetNodeWithNameOfChildThatDoesNotExist() throws Exception {
+ node.getNode("something");
}
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowGetNodeWithNoPath() throws Exception {
+ node.getNode(null);
+ }
+
@Test
- public void shouldProvideProperty() throws Exception {
- Property prop1 = Mockito.mock(Property.class);
- stub(prop1.getName()).toReturn("prop1");
- properties.put(name(prop1.getName()), prop1);
- assertThat(node.getProperty("prop1"), is(prop1));
- MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
- Map<Name, Property> properties = new HashMap<Name, Property>();
- child.setProperties(properties);
- Property prop2 = Mockito.mock(Property.class);
- stub(prop2.getName()).toReturn("prop2");
- stub(session.getItem("/node/child/prop2")).toReturn(prop2);
- properties.put(name(prop2.getName()), prop2);
- MockAbstractJcrNode prop3Node = createChild(session, "prop3", 1, children, child);
- node.setChildren(children);
- assertThat(node.getProperty("child/prop2"), is(prop2));
- // Ensure we return a property even when a child exists with the same name
- Property prop3 = Mockito.mock(Property.class);
- stub(prop3.getName()).toReturn("prop3");
- properties.put(name(prop3.getName()), prop3);
- stub(session.getItem("/node/child/prop3")).toReturn(prop3Node);
- assertThat(node.getProperty("child/prop3"), is(prop3));
+ public void shouldProvideNodeIterator() throws Exception {
+ AbstractJcrNode child = mock(AbstractJcrNode.class);
+ UUID childUuid = UUID.randomUUID();
+ children.append(cache, name("child"), childUuid);
+ stub(cache.findJcrNode(childUuid)).toReturn(child);
+ NodeIterator iter = node.getNodes();
+ assertThat(iter, notNullValue());
+ assertThat(iter.getSize(), is(1L));
+ assertThat(iter.next(), is((Object)child));
}
- @Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowGetPropertyWithNullPath() throws Exception {
- node.getProperty((String)null);
+ @Test
+ public void shoudReturnItemFromGetPrimaryItemIfItExists() throws Exception {
+ // Define the node type ...
+ Name primaryTypeName = name("thePrimaryType");
+ JcrNodeType primaryType = mock(JcrNodeType.class);
+ stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+ stub(primaryType.getPrimaryItemName()).toReturn("thePrimaryItemName");
+ // Define the node info to use this primary type ...
+ stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+ // Now make an item with the appropriate name ...
+ AbstractJcrNode child = mock(AbstractJcrNode.class);
+ stub(cache.findJcrItem(uuid, path("thePrimaryItemName"))).toReturn(child);
+ // Now call the method ...
+ assertThat(node.getPrimaryItem(), is((Item)child));
}
- @Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowGetPropertyWithEmptyPath() throws Exception {
- node.getProperty("");
+ @Test( expected = ItemNotFoundException.class )
+ public void shoudFailToReturnItemFromGetPrimaryItemIfPrimaryTypeDoesNotHavePrimaryItemName() throws Exception {
+ // Define the node type ...
+ Name primaryTypeName = name("thePrimaryType");
+ JcrNodeType primaryType = mock(JcrNodeType.class);
+ stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+ stub(primaryType.getPrimaryItemName()).toReturn(null);
+ // Define the node info to use this primary type ...
+ stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+ // Now call the method ...
+ node.getPrimaryItem();
}
- @Test( expected = PathNotFoundException.class )
- public void shouldNotProvideChildPropertyIfNotAvailable() throws Exception {
- node.getProperty("prop1");
+ @Test( expected = ItemNotFoundException.class )
+ public void shoudFailToReturnItemFromGetPrimaryItemIfThePrimaryTypeAsInvalidPrimaryItemName() throws Exception {
+ // Define the node type ...
+ Name primaryTypeName = name("thePrimaryType");
+ JcrNodeType primaryType = mock(JcrNodeType.class);
+ stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+ stub(primaryType.getPrimaryItemName()).toReturn("/this/is/not/valid");
+ // Define the node info to use this primary type ...
+ stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+ // Now call the method ...
+ node.getPrimaryItem();
}
- @Test( expected = PathNotFoundException.class )
- public void shouldNotProvideDescendentPropertyIfNotAvailable() throws Exception {
- MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
- Map<Name, Property> properties = new HashMap<Name, Property>();
- child.setProperties(properties);
- MockAbstractJcrNode propNode = createChild(session, "prop", 1, children, child);
- node.setChildren(children);
- stub(session.getItem("/node/child/prop")).toReturn(propNode);
- node.getProperty("child/prop");
+ @Test( expected = ItemNotFoundException.class )
+ public void shoudFailToReturnItemFromGetPrimaryItemIfTheNodeHasNoItemMatchingThatSpecifiedByThePrimaryType() throws Exception {
+ // Define the node type ...
+ Name primaryTypeName = name("thePrimaryType");
+ JcrNodeType primaryType = mock(JcrNodeType.class);
+ stub(nodeTypes.getNodeType(primaryTypeName)).toReturn(primaryType);
+ stub(primaryType.getPrimaryItemName()).toReturn("thePrimaryItemName");
+ // Define the node info to use this primary type ...
+ stub(info.getPrimaryTypeName()).toReturn(primaryTypeName);
+ // Now call the method ...
+ stub(cache.findJcrItem(uuid, path("thePrimaryItemName"))).toThrow(new ItemNotFoundException());
+ node.getPrimaryItem();
}
@Test
- public void shouldNotAllowGetReferences() throws Exception {
+ public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoProperties() throws Exception {
+ // Set up two properties (one containing references, the other not) ...
+ List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {});
+ stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+ // Now call the method ...
PropertyIterator iter = node.getReferences();
- assertThat(iter, is(notNullValue()));
assertThat(iter.getSize(), is(0L));
+ assertThat(iter.hasNext(), is(false));
}
@Test
+ public void shouldReturnEmptyIteratorFromGetReferencesWhenThereAreNoReferenceProperties() throws Exception {
+ // Set up two properties (one containing references, the other not) ...
+ AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
+ AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
+ List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
+ stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+ stub(propertyA.getType()).toReturn(PropertyType.LONG);
+ stub(propertyB.getType()).toReturn(PropertyType.BOOLEAN);
+ // Now call the method ...
+ PropertyIterator iter = node.getReferences();
+ assertThat(iter.getSize(), is(0L));
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldReturnIteratorFromGetReferencesWhenThereIsAtLeastOneReferenceProperty() throws Exception {
+ // Set up two properties (one containing references, the other not) ...
+ AbstractJcrProperty propertyA = mock(AbstractJcrProperty.class);
+ AbstractJcrProperty propertyB = mock(AbstractJcrProperty.class);
+ List<AbstractJcrProperty> props = Arrays.asList(new AbstractJcrProperty[] {propertyA, propertyB});
+ stub(cache.findJcrPropertiesFor(uuid)).toReturn(props);
+ stub(propertyA.getType()).toReturn(PropertyType.LONG);
+ stub(propertyB.getType()).toReturn(PropertyType.REFERENCE);
+ // Now call the method ...
+ PropertyIterator iter = node.getReferences();
+ assertThat(iter.getSize(), is(1L));
+ assertThat(iter.next(), is(sameInstance((Object)propertyB)));
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
public void shouldProvideSession() throws Exception {
assertThat((JcrSession)node.getSession(), is(session));
}
@Test
public void shouldProvideUuidIfReferenceable() throws Exception {
- String uuid = "uuid";
- Property mixinProp = Mockito.mock(Property.class);
- stub(mixinProp.getName()).toReturn("jcr:mixinTypes");
- Value value = Mockito.mock(Value.class);
- stub(value.getString()).toReturn("mix:referenceable");
- stub(mixinProp.getValues()).toReturn(new Value[] {value});
- properties.put(name(mixinProp.getName()), mixinProp);
- Property uuidProp = Mockito.mock(Property.class);
- stub(uuidProp.getName()).toReturn("jcr:uuid");
- stub(uuidProp.getString()).toReturn(uuid);
- properties.put(name(uuidProp.getName()), uuidProp);
- assertThat(node.getUUID(), is(uuid));
+ // Create the property ...
+ PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrProperty(propertyId)).toReturn(property);
+ stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:referenceable")});
+ // Call the method ...
+ assertThat(node.getUUID(), is(uuid.toString()));
}
@Test( expected = UnsupportedRepositoryOperationException.class )
public void shouldNotProvideUuidIfNotReferenceable() throws Exception {
- String uuid = "uuid";
- Property mixinProp = Mockito.mock(Property.class);
- stub(mixinProp.getName()).toReturn("jcr:mixinTypes");
- Value value = Mockito.mock(Value.class);
- stub(mixinProp.getValues()).toReturn(new Value[] {value});
- properties.put(name(mixinProp.getName()), mixinProp);
- Property uuidProp = Mockito.mock(Property.class);
- stub(uuidProp.getName()).toReturn("jcr:uuid");
- stub(uuidProp.getString()).toReturn(uuid);
- properties.put(name(uuidProp.getName()), uuidProp);
+ // Create the property ...
+ PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrProperty(propertyId)).toReturn(property);
+ stub(property.getValues()).toReturn(new Value[] {stringValueFor("acme:someMixin"), stringValueFor("mix:notReferenceable")});
+ // Call the method ...
node.getUUID();
}
@Test( expected = UnsupportedRepositoryOperationException.class )
public void shouldNotProvideUuidIfNoMixinTypes() throws Exception {
- String uuid = "uuid";
- Property uuidProp = Mockito.mock(Property.class);
- stub(uuidProp.getName()).toReturn("jcr:uuid");
- stub(uuidProp.getString()).toReturn(uuid);
- properties.put(name(uuidProp.getName()), uuidProp);
+ PropertyId propertyId = new PropertyId(uuid, name("jcr:mixinTypes"));
+ stub(cache.findJcrProperty(propertyId)).toReturn(null);
+ // Call the method ...
node.getUUID();
}
@@ -395,76 +483,134 @@
node.getVersionHistory();
}
- @Test
- public void shouldProvideHasNode() throws Exception {
- assertThat(node.hasNode("{}child"), is(false));
- Property prop = Mockito.mock(Property.class);
- stub(prop.getName()).toReturn("prop");
- properties.put(name(prop.getName()), prop);
- assertThat(node.hasNode("prop"), is(false));
- AbstractJcrNode child = createChild(session, "child", 1, children, node);
- AbstractJcrNode child2 = createChild(session, "child2", 1, children, child);
- node.setChildren(children);
- assertThat(node.hasNode("child"), is(true));
- stub(session.getItem("/node/child/{}child2")).toReturn(child2);
- assertThat(node.hasNode("child/{}child2"), is(true));
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowNullPathInHasNode() throws Exception {
+ node.hasNode((String)null);
}
@Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowHasNodeWithNoPath() throws Exception {
- node.hasNode(null);
+ public void shouldNotAllowEmptyPathInHasNode() throws Exception {
+ node.hasNode("");
}
@Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowHasNodeWithEmptyPath() throws Exception {
- node.hasNode("");
+ public void shouldNotAbsolutePathInHasNode() throws Exception {
+ node.hasNode("/a/b/c");
}
@Test
- public void shouldProvideHasNodes() throws Exception {
- assertThat(node.hasNodes(), is(false));
- createChild(session, "child", 1, children, node);
- node.setChildren(children);
- assertThat(node.hasNodes(), is(true));
+ public void shouldReturnTrueFromHasNodeWithSelfReference() throws Exception {
+ assertThat(node.hasNode("."), is(true));
}
@Test
- public void shouldProvideHasProperties() throws Exception {
- assertThat(node.hasProperties(), is(false));
- properties.put(name("something"), Mockito.mock(Property.class));
- assertThat(node.hasProperties(), is(true));
+ public void shouldReturnTrueFromHasNodeWithParentReferenceIfNotRootNode() throws Exception {
+ assertThat(node.hasNode(".."), is(!node.isRoot()));
}
@Test
- public void shouldIndicateHasProperty() throws Exception {
- assertThat(node.hasProperty("prop"), is(false));
- MockAbstractJcrNode child = createChild(session, "child", 1, children, node);
- node.setChildren(children);
- assertThat(node.hasProperty("child"), is(false));
- Property prop = Mockito.mock(Property.class);
- stub(prop.getName()).toReturn("prop");
- properties.put(name(prop.getName()), prop);
- assertThat(node.hasProperty("prop"), is(true));
- Map<Name, Property> properties = new HashMap<Name, Property>();
- child.setProperties(properties);
- Property prop2 = Mockito.mock(Property.class);
- stub(prop2.getName()).toReturn("prop2");
- properties.put(name(prop2.getName()), prop2);
- stub(session.getItem("/node/child/prop2")).toReturn(prop2);
- assertThat(node.hasProperty("child/prop2"), is(true));
+ public void shouldReturnTrueFromHasNodeIfRelativePathIdentifiesExistingNode() throws Exception {
+ AbstractJcrNode otherNode = mock(AbstractJcrNode.class);
+ stub(cache.findJcrNode(uuid, path("../other/node"))).toReturn(otherNode);
+ assertThat(node.hasNode("../other/node"), is(true));
}
+ @Test
+ public void shouldReturnTrueFromHasNodeIfChildWithNameExists() throws Exception {
+ children.append(cache, name("child"), UUID.randomUUID());
+ assertThat(node.hasNode("child[1]"), is(true));
+ assertThat(node.hasNode("child[2]"), is(false));
+ children.append(cache, name("child"), UUID.randomUUID());
+ assertThat(node.hasNode("child[2]"), is(true));
+ assertThat(node.hasNode("child[3]"), is(false));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasNodeWithNameOfChildThatDoesNotExist() throws Exception {
+ assertThat(node.hasNode("something"), is(false));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasNodesIfThereAreNoChildren() throws Exception {
+ assertThat(node.hasNodes(), is(false));
+ }
+
+ @Test
+ public void shouldReturnTrueFromHasNodesIfThereIsAtLeastOneChild() throws Exception {
+ children.append(cache, name("child"), UUID.randomUUID());
+ assertThat(node.hasNodes(), is(true));
+ }
+
@Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowHasPropertyWithNoPath() throws Exception {
- node.hasProperty(null);
+ public void shouldNotAllowNullPathInHasProperty() throws Exception {
+ node.hasProperty((String)null);
}
@Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowHasPropertyWithEmptyPath() throws Exception {
+ public void shouldNotAllowEmptyPathInHasProperty() throws Exception {
node.hasProperty("");
}
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAbsolutePathInHasProperty() throws Exception {
+ node.hasProperty("/a/b/c");
+ }
+
@Test
+ public void shouldReturnTrueFromHasPropertyIfPathIsRelativePathToOtherNodeWithNamedProperty() throws Exception {
+ AbstractJcrProperty property = mock(AbstractJcrProperty.class);
+ stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toReturn(property);
+ assertThat(node.hasProperty("../good/path"), is(true));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasPropertyIfPathIsRelativePathToOtherNodeWithoutNamedProperty() throws Exception {
+ stub(cache.findJcrItem(uuid, relativePath("../good/path"))).toThrow(new PathNotFoundException());
+ assertThat(node.hasProperty("../good/path"), is(false));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasPropertyIfPathIsParentPath() throws Exception {
+ assertThat(node.hasProperty(".."), is(false));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasPropertyIfPathIsSelfPath() throws Exception {
+ assertThat(node.hasProperty("."), is(false));
+ }
+
+ @Test
+ public void shouldReturnTrueFromHasPropertyIfPathIsNameAndNodeHasProperty() throws Exception {
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ stub(cache.findPropertyInfo(new PropertyId(uuid, name("prop")))).toReturn(propertyInfo);
+ assertThat(node.hasProperty("prop"), is(true));
+ }
+
+ @Test
+ public void shouldReturnFalseFromHasPropertyIfPathIsNameAndNodeDoesNotHaveProperty() throws Exception {
+ stub(cache.findPropertyInfo(new PropertyId(uuid, name("prop")))).toReturn(null);
+ assertThat(node.hasProperty("prop"), is(false));
+ }
+
+ @Test
+ @SuppressWarnings( "unchecked" )
+ public void shouldReturnTrueFromHasPropertiesIfNodeHasAtLeastOneProperty() throws Exception {
+ Map<Name, PropertyInfo> properties = mock(Map.class);
+ stub(properties.size()).toReturn(5);
+ stub(info.getProperties()).toReturn(properties);
+ assertThat(node.hasProperties(), is(true));
+ }
+
+ @Test
+ @SuppressWarnings( "unchecked" )
+ public void shouldReturnFalseFromHasPropertiesIfNodeHasNoProperties() throws Exception {
+ Map<Name, PropertyInfo> properties = mock(Map.class);
+ stub(properties.size()).toReturn(0);
+ stub(info.getProperties()).toReturn(properties);
+ assertThat(node.hasProperties(), is(false));
+ }
+
+ @Test
public void shouldNotAllowHoldsLock() throws Exception {
assertThat(node.holdsLock(), is(false));
}
@@ -485,29 +631,87 @@
}
@Test
- public void shouldProvideIsSame() throws Exception {
- stub(session.getWorkspace()).toReturn(Mockito.mock(Workspace.class));
- stub(session.getRepository()).toReturn(repository);
+ public void shouldReturnFalseFromIsSameIfTheRepositoryInstanceIsDifferent() throws Exception {
+ Workspace workspace1 = mock(Workspace.class);
+ Repository repository1 = mock(Repository.class);
+ stub(session.getWorkspace()).toReturn(workspace1);
+ stub(session.getRepository()).toReturn(repository1);
+ stub(workspace1.getName()).toReturn("workspace1");
+
+ Workspace workspace2 = mock(Workspace.class);
JcrSession session2 = Mockito.mock(JcrSession.class);
- JcrRepository repository2 = Mockito.mock(JcrRepository.class);
+ Repository repository2 = mock(Repository.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session2.getRepository()).toReturn(repository2);
+ stub(workspace2.getName()).toReturn("workspace1");
+ stub(cache2.session()).toReturn(session2);
- stub(session2.getRepository()).toReturn(repository2);
- stub(session2.getWorkspace()).toReturn(workspace);
- Node node2 = new MockAbstractJcrNode(session2, node.getName(), node.getParent());
+ UUID uuid2 = uuid;
+ Node node2 = new MockAbstractJcrNode(cache2, uuid2);
assertThat(node.isSame(node2), is(false));
+ }
- Property prop = Mockito.mock(Property.class);
- stub(prop.getSession()).toReturn(session);
- assertThat(node.isSame(prop), is(false));
- node2 = Mockito.mock(Node.class);
- stub(node2.getSession()).toReturn(session);
+ @Test
+ public void shouldReturnFalseFromIsSameIfTheWorkspaceNameIsDifferent() throws Exception {
+ Workspace workspace1 = mock(Workspace.class);
+ Repository repository1 = mock(Repository.class);
+ stub(session.getWorkspace()).toReturn(workspace1);
+ stub(session.getRepository()).toReturn(repository1);
+ stub(workspace1.getName()).toReturn("workspace1");
+
+ Workspace workspace2 = mock(Workspace.class);
+ JcrSession session2 = Mockito.mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session2.getRepository()).toReturn(repository1);
+ stub(workspace2.getName()).toReturn("workspace2");
+ stub(cache2.session()).toReturn(session2);
+
+ UUID uuid2 = uuid;
+ Node node2 = new MockAbstractJcrNode(cache2, uuid2);
assertThat(node.isSame(node2), is(false));
- node2 = new MockAbstractJcrNode(session, node.getName(), node.getParent());
- UUID uuid = UUID.randomUUID();
- node.setInternalUuid(uuid);
- ((MockAbstractJcrNode)node2).setInternalUuid(UUID.randomUUID());
+ }
+
+ @Test
+ public void shouldReturnFalseFromIsSameIfTheNodeUuidIsDifferent() throws Exception {
+ Workspace workspace1 = mock(Workspace.class);
+ Repository repository1 = mock(Repository.class);
+ stub(session.getWorkspace()).toReturn(workspace1);
+ stub(session.getRepository()).toReturn(repository1);
+ stub(workspace1.getName()).toReturn("workspace1");
+
+ Workspace workspace2 = mock(Workspace.class);
+ JcrSession session2 = Mockito.mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session2.getRepository()).toReturn(repository1);
+ stub(workspace2.getName()).toReturn("workspace1");
+ stub(cache2.session()).toReturn(session2);
+
+ UUID uuid2 = UUID.randomUUID();
+ Node node2 = new MockAbstractJcrNode(cache2, uuid2);
assertThat(node.isSame(node2), is(false));
- ((MockAbstractJcrNode)node2).setInternalUuid(uuid);
+ }
+
+ @Test
+ public void shouldReturnTrueFromIsSameIfTheNodeUuidAndWorkspaceNameAndRepositoryInstanceAreSame() throws Exception {
+ Workspace workspace1 = mock(Workspace.class);
+ Repository repository1 = mock(Repository.class);
+ stub(session.getWorkspace()).toReturn(workspace1);
+ stub(session.getRepository()).toReturn(repository1);
+ stub(workspace1.getName()).toReturn("workspace1");
+
+ Workspace workspace2 = mock(Workspace.class);
+ JcrSession session2 = Mockito.mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session2.getRepository()).toReturn(repository1);
+ stub(workspace2.getName()).toReturn("workspace1");
+ stub(cache2.session()).toReturn(session2);
+
+ UUID uuid2 = uuid;
+ Node node2 = new MockAbstractJcrNode(cache2, uuid2);
assertThat(node.isSame(node2), is(true));
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/AbstractJcrPropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -25,6 +25,7 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import java.io.InputStream;
import java.util.Calendar;
@@ -34,17 +35,16 @@
import javax.jcr.ItemVisitor;
import javax.jcr.Node;
import javax.jcr.Property;
-import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeDefinition;
-import javax.jcr.nodetype.PropertyDefinition;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@@ -56,48 +56,54 @@
*/
public class AbstractJcrPropertyTest {
+ private PropertyId propertyId;
+ private PropertyInfo info;
+ private ExecutionContext executionContext;
+ private JcrNode node;
private AbstractJcrProperty prop;
@Mock
- private Workspace workspace;
- @Mock
- private Repository repository;
- @Mock
private JcrSession session;
- private AbstractJcrNode node;
@Mock
- private NodeDefinition nodeDefinition;
- @Mock
- private PropertyDefinition propertyDefinition;
- private ExecutionContext executionContext;
- private org.jboss.dna.graph.property.Property dnaProperty;
- private Location rootLocation;
- private Location nodeLocation;
+ private SessionCache cache;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
executionContext = new ExecutionContext();
- dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
- stub(propertyDefinition.getRequiredType()).toReturn(PropertyType.STRING);
- stub(session.getWorkspace()).toReturn(workspace);
- stub(session.getRepository()).toReturn(repository);
stub(session.getExecutionContext()).toReturn(executionContext);
- UUID rootUuid = UUID.randomUUID();
- Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
- rootLocation = Location.create(rootPath, rootUuid);
- JcrRootNode rootNode = new JcrRootNode(session, rootLocation, nodeDefinition);
- stub(session.getNode(rootUuid)).toReturn(rootNode);
-
UUID uuid = UUID.randomUUID();
- Path path = executionContext.getValueFactories().getPathFactory().create("/nodeName");
- nodeLocation = Location.create(path, uuid);
- node = new JcrNode(session, rootUuid, nodeLocation, nodeDefinition);
- stub(session.getNode(uuid)).toReturn(node);
+ node = new JcrNode(cache, uuid);
+ propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+ prop = new MockAbstractJcrProperty(cache, propertyId);
- prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
+ info = mock(PropertyInfo.class);
+ stub(info.getPropertyId()).toReturn(propertyId);
+ stub(info.getPropertyName()).toReturn(propertyId.getPropertyName());
+
+ stub(cache.session()).toReturn(session);
+ stub(cache.context()).toReturn(executionContext);
+ stub(cache.findJcrProperty(propertyId)).toReturn(prop);
+ stub(cache.findPropertyInfo(propertyId)).toReturn(info);
+ stub(cache.getPathFor(info)).toReturn(path("/a/b/c/jcr:mimeType"));
+ stub(cache.getPathFor(propertyId)).toReturn(path("/a/b/c/jcr:mimeType"));
+ stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c"));
+
+ NodeInfo nodeInfo = mock(NodeInfo.class);
+ stub(cache.findJcrNode(uuid)).toReturn(node);
+ stub(cache.findNodeInfo(uuid)).toReturn(nodeInfo);
+ stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c"));
+ stub(cache.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
}
+ protected Name name( String name ) {
+ return executionContext.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String path ) {
+ return executionContext.getValueFactories().getPathFactory().create(path);
+ }
+
@Test
public void shouldAllowVisitation() throws Exception {
ItemVisitor visitor = Mockito.mock(ItemVisitor.class);
@@ -123,18 +129,18 @@
}
@Test( expected = ItemNotFoundException.class )
- public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
- prop.getAncestor(3);
+ public void shouldNotAllowAncestorDepthGreaterThanPropertyDepth() throws Exception {
+ prop.getAncestor(prop.getDepth() + 1);
}
@Test
public void shouldProvideDepth() throws Exception {
- assertThat(prop.getDepth(), is(2));
+ assertThat(prop.getDepth(), is(4));
}
@Test
public void shouldProvideExecutionContext() throws Exception {
- assertThat(prop.getExecutionContext(), is(executionContext));
+ assertThat(prop.context(), is(executionContext));
}
@Test
@@ -149,7 +155,7 @@
@Test
public void shouldProvidePath() throws Exception {
- assertThat(prop.getPath(), is("/nodeName/jcr:mimeType"));
+ assertThat(prop.getPath(), is("/a/b/c/jcr:mimeType"));
}
@Test
@@ -163,43 +169,111 @@
}
@Test
- public void shouldIndicateSameAsNodeWithSameParentAndSamePropertyName() throws Exception {
- org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
- .create(dnaProperty.getName());
+ public void shouldIndicateSameAsPropertyWithSameNodeAndSamePropertyName() throws Exception {
+ Repository repository = mock(Repository.class);
+ Workspace workspace = mock(Workspace.class);
+ Workspace workspace2 = mock(Workspace.class);
+ stub(workspace.getName()).toReturn("workspace");
+ stub(workspace2.getName()).toReturn("workspace");
+ JcrSession session2 = mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getRepository()).toReturn(repository);
+ stub(session.getRepository()).toReturn(repository);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session.getWorkspace()).toReturn(workspace);
+ stub(cache2.session()).toReturn(session2);
+ stub(cache2.context()).toReturn(executionContext);
+
// Make the other node have the same UUID ...
- JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+ UUID uuid = node.internalUuid();
+ NodeInfo nodeInfo = mock(NodeInfo.class);
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+ stub(propertyInfo.getPropertyId()).toReturn(propertyId);
+ stub(propertyInfo.getPropertyName()).toReturn(propertyId.getPropertyName());
+ stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+ stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+ stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+ stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+ stub(cache2.findPropertyInfo(propertyId)).toReturn(info);
assertThat(node.isSame(otherNode), is(true));
- Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
- Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+ Property prop = new MockAbstractJcrProperty(cache, propertyId);
+ Property otherProp = new MockAbstractJcrProperty(cache2, propertyId);
assertThat(prop.isSame(otherProp), is(true));
}
@Test
public void shouldIndicateDifferentThanNodeWithDifferentParent() throws Exception {
- org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory()
- .create(dnaProperty.getName());
- UUID otherUuid = UUID.randomUUID();
- Path otherPath = executionContext.getValueFactories().getPathFactory().create("/nodeName");
- Location location = Location.create(otherPath, otherUuid);
- JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), location, nodeDefinition);
- stub(session.getNode(otherUuid)).toReturn(otherNode);
+ Repository repository = mock(Repository.class);
+ Workspace workspace = mock(Workspace.class);
+ Workspace workspace2 = mock(Workspace.class);
+ stub(workspace.getName()).toReturn("workspace");
+ stub(workspace2.getName()).toReturn("workspace");
+ JcrSession session2 = mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getRepository()).toReturn(repository);
+ stub(session.getRepository()).toReturn(repository);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session.getWorkspace()).toReturn(workspace);
+ stub(cache2.session()).toReturn(session2);
+ stub(cache2.context()).toReturn(executionContext);
+ // Make the other node have a different UUID ...
+ UUID uuid = UUID.randomUUID();
+ PropertyId propertyId2 = new PropertyId(uuid, JcrLexicon.MIXIN_TYPES);
+ NodeInfo nodeInfo = mock(NodeInfo.class);
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+ stub(propertyInfo.getPropertyId()).toReturn(propertyId2);
+ stub(propertyInfo.getPropertyName()).toReturn(propertyId2.getPropertyName());
+ stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+ stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+ stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+ stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+
assertThat(node.isSame(otherNode), is(false));
- Property prop = new MockAbstractJcrProperty(node, propertyDefinition, otherDnaProperty);
- Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+ Property prop = new MockAbstractJcrProperty(cache, propertyId);
+ Property otherProp = new MockAbstractJcrProperty(cache2, propertyId2);
assertThat(prop.isSame(otherProp), is(false));
}
@Test
public void shouldIndicateDifferentThanPropertyWithSameNodeWithDifferentPropertyName() throws Exception {
- org.jboss.dna.graph.property.Property otherDnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.NAME);
+ Repository repository = mock(Repository.class);
+ Workspace workspace = mock(Workspace.class);
+ Workspace workspace2 = mock(Workspace.class);
+ stub(workspace.getName()).toReturn("workspace");
+ stub(workspace2.getName()).toReturn("workspace");
+ JcrSession session2 = mock(JcrSession.class);
+ SessionCache cache2 = mock(SessionCache.class);
+ stub(session2.getRepository()).toReturn(repository);
+ stub(session.getRepository()).toReturn(repository);
+ stub(session2.getWorkspace()).toReturn(workspace2);
+ stub(session.getWorkspace()).toReturn(workspace);
+ stub(cache2.session()).toReturn(session2);
+ stub(cache2.context()).toReturn(executionContext);
+
// Make the other node have the same UUID ...
- JcrNode otherNode = new JcrNode(session, rootLocation.getUuid(), nodeLocation, nodeDefinition);
+ UUID uuid = node.internalUuid();
+ NodeInfo nodeInfo = mock(NodeInfo.class);
+ PropertyInfo propertyInfo = mock(PropertyInfo.class);
+ PropertyId propertyId2 = new PropertyId(uuid, JcrLexicon.NAME);
+ AbstractJcrNode otherNode = new JcrNode(cache2, uuid);
+ stub(propertyInfo.getPropertyId()).toReturn(propertyId2);
+ stub(propertyInfo.getPropertyName()).toReturn(propertyId2.getPropertyName());
+ stub(cache2.findJcrNode(uuid)).toReturn(otherNode);
+ stub(cache2.findNodeInfo(uuid)).toReturn(nodeInfo);
+ stub(cache2.getPathFor(uuid)).toReturn(path("/a/b/c"));
+ stub(cache2.getPathFor(nodeInfo)).toReturn(path("/a/b/c"));
+ stub(cache2.findPropertyInfo(propertyId2)).toReturn(propertyInfo);
assertThat(node.isSame(otherNode), is(true));
- Property prop = new MockAbstractJcrProperty(node, propertyDefinition, dnaProperty);
- Property otherProp = new MockAbstractJcrProperty(otherNode, propertyDefinition, otherDnaProperty);
+
+ Property prop = new MockAbstractJcrProperty(cache, propertyId);
+ Property otherProp = new MockAbstractJcrProperty(cache2, propertyId2);
assertThat(prop.isSame(otherProp), is(false));
}
@@ -255,10 +329,9 @@
private class MockAbstractJcrProperty extends AbstractJcrProperty {
- MockAbstractJcrProperty( AbstractJcrNode node,
- PropertyDefinition propertyDefinition,
- org.jboss.dna.graph.property.Property dnaProperty ) {
- super(node, propertyDefinition, propertyDefinition.getRequiredType(), dnaProperty);
+ MockAbstractJcrProperty( SessionCache cache,
+ PropertyId propertyId ) {
+ super(cache, propertyId);
}
/**
@@ -266,8 +339,9 @@
*
* @see javax.jcr.Property#getNode()
*/
+ @SuppressWarnings( "synthetic-access" )
public Node getNode() {
- throw new UnsupportedOperationException(); // shouldn't be called
+ return node;
}
/**
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,112 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import org.jboss.dna.graph.property.basic.BasicName;
+import org.jboss.dna.jcr.SessionCache.ChildNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class JcrChildNodeIteratorTest {
+
+ private List<ChildNode> children;
+ private List<Node> childNodes;
+ private NodeIterator iter;
+ @Mock
+ private SessionCache cache;
+
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ children = new ArrayList<ChildNode>();
+ childNodes = new ArrayList<Node>();
+ for (int i = 0; i != 10; ++i) {
+ UUID uuid = UUID.randomUUID();
+ ChildNode child = new ChildNode(uuid, new BasicName("", "name"), i + 1);
+ AbstractJcrNode node = mock(AbstractJcrNode.class);
+ stub(cache.findJcrNode(uuid)).toReturn(node);
+ children.add(child);
+ childNodes.add(node);
+ }
+ iter = new JcrChildNodeIterator(cache, children, children.size());
+ }
+
+ @Test
+ public void shouldProperlyDetermineHasNext() {
+ Iterator<Node> nodeIter = childNodes.iterator();
+ long position = 0L;
+ assertThat(iter.getPosition(), is(position));
+ while (iter.hasNext()) {
+ assertThat(nodeIter.hasNext(), is(true));
+ Node actual = (Node)iter.next();
+ Node expected = nodeIter.next();
+ assertThat(iter.getPosition(), is(++position));
+ assertThat(iter.getPosition(), is(position)); // call twice
+ assertThat(actual, is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ assertThat(nodeIter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldStartWithPositionOfZero() {
+ assertThat(iter.getPosition(), is(0L));
+ }
+
+ @Test
+ public void shouldHaveCorrectSize() {
+ assertThat(iter.getSize(), is((long)childNodes.size()));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldNotAllowRemove() {
+ iter.remove();
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailWhenNextIsCalled() {
+ while (iter.hasNext()) {
+ iter.next();
+ }
+ iter.next();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrChildNodeIteratorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,70 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.NoSuchElementException;
+import javax.jcr.NodeIterator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class JcrEmptyNodeIteratorTest {
+
+ private NodeIterator iter;
+
+ @Before
+ public void beforeEach() {
+ iter = new JcrEmptyNodeIterator();
+ }
+
+ @Test
+ public void shouldNotHaveNext() {
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldHavePositionOfZero() {
+ assertThat(iter.getPosition(), is(0L));
+ }
+
+ @Test
+ public void shouldHaveSizeOfZero() {
+ assertThat(iter.getSize(), is(0L));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldNotAllowRemove() {
+ iter.remove();
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailWhenNextIsCalled() {
+ iter.next();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyNodeIteratorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,70 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.NoSuchElementException;
+import javax.jcr.PropertyIterator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class JcrEmptyPropertyIteratorTest {
+
+ private PropertyIterator iter;
+
+ @Before
+ public void beforeEach() {
+ iter = new JcrEmptyPropertyIterator();
+ }
+
+ @Test
+ public void shouldNotHaveNext() {
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldHavePositionOfZero() {
+ assertThat(iter.getPosition(), is(0L));
+ }
+
+ @Test
+ public void shouldHaveSizeOfZero() {
+ assertThat(iter.getSize(), is(0L));
+ }
+
+ @Test( expected = UnsupportedOperationException.class )
+ public void shouldNotAllowRemove() {
+ iter.remove();
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void shouldFailWhenNextIsCalled() {
+ iter.next();
+ }
+
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrEmptyPropertyIteratorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrMultiValuePropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -28,15 +28,13 @@
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.stub;
import java.util.UUID;
-import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.PropertyDefinition;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -47,34 +45,50 @@
*/
public class JcrMultiValuePropertyTest {
- private Property prop;
- private AbstractJcrNode node;
+ private PropertyId propertyId;
+ private JcrMultiValueProperty prop;
private ExecutionContext executionContext;
+ private org.jboss.dna.graph.property.Property dnaProperty;
@Mock
+ private SessionCache cache;
+ @Mock
private JcrSession session;
@Mock
- private NodeDefinition nodeDefinition;
+ private PropertyInfo propertyInfo;
@Mock
- private PropertyDefinition definition;
- private org.jboss.dna.graph.property.Property dnaProperty;
+ private JcrPropertyDefinition definition;
+ @Mock
+ private JcrNodeTypeManager nodeTypes;
@Before
- public void before() {
+ public void before() throws Exception {
MockitoAnnotations.initMocks(this);
executionContext = new ExecutionContext();
+ stub(cache.session()).toReturn(session);
+ stub(cache.context()).toReturn(executionContext);
+ stub(session.nodeTypeManager()).toReturn(nodeTypes);
- UUID rootUuid = UUID.randomUUID();
- Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
- Location rootLocation = Location.create(rootPath, rootUuid);
- node = new JcrRootNode(session, rootLocation, nodeDefinition);
- stub(session.getExecutionContext()).toReturn(executionContext);
-
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, true);
stub(definition.getRequiredType()).toReturn(PropertyType.BOOLEAN);
stub(definition.isMultiple()).toReturn(true);
- prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+ PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
+ stub(nodeTypes.getPropertyDefinition(definitionId, true)).toReturn(definition);
+
+ UUID uuid = UUID.randomUUID();
+ propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+ prop = new JcrMultiValueProperty(cache, propertyId);
+
+ stub(cache.findPropertyInfo(propertyId)).toReturn(propertyInfo);
+ stub(propertyInfo.getDefinitionId()).toReturn(definitionId);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BOOLEAN);
+ stub(propertyInfo.isMultiValued()).toReturn(true);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
}
+ protected Name name( String name ) {
+ return executionContext.getValueFactories().getNameFactory().create(name);
+ }
+
@Test
public void shouldProvideAppropriateType() throws Exception {
assertThat(prop.getType(), is(definition.getRequiredType()));
@@ -82,7 +96,7 @@
@Test
public void shouldProvidePropertyDefinition() throws Exception {
- assertThat(prop.getDefinition(), notNullValue());
+ assertThat(prop.getDefinition(), is((PropertyDefinition)definition));
}
@Test
@@ -149,9 +163,10 @@
Object value = "value";
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
stub(definition.isMultiple()).toReturn(true);
- prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+ prop = new JcrMultiValueProperty(cache, propertyId);
lengths = prop.getLengths();
assertThat(lengths, notNullValue());
assertThat(lengths.length, is(1));
@@ -160,9 +175,10 @@
value = new Object();
long expectedLength = executionContext.getValueFactories().getBinaryFactory().create(value).getSize();
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, value);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
stub(definition.isMultiple()).toReturn(true);
- prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+ prop = new JcrMultiValueProperty(cache, propertyId);
lengths = prop.getLengths();
assertThat(lengths, notNullValue());
assertThat(lengths.length, is(1));
@@ -170,9 +186,10 @@
String[] values = new String[] {"value1", "value2", "value 3 is longer"};
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object[])values);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
stub(definition.isMultiple()).toReturn(true);
- prop = new JcrMultiValueProperty(node, definition, definition.getRequiredType(), dnaProperty);
+ prop = new JcrMultiValueProperty(cache, propertyId);
lengths = prop.getLengths();
assertThat(lengths, notNullValue());
assertThat(lengths.length, is(values.length));
Deleted: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -1,101 +0,0 @@
-/*
- * JBoss DNA (http://www.jboss.org/dna)
- * See the COPYRIGHT.txt file distributed with this work for information
- * regarding copyright ownership. Some portions may be licensed
- * to Red Hat, Inc. under one or more contributor license agreements.
- * See the AUTHORS.txt file in the distribution for a full listing of
- * individual contributors.
- *
- * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- * is licensed to you under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * JBoss DNA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.dna.jcr;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.stub;
-import java.util.ArrayList;
-import java.util.List;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
-
-/**
- * @author jverhaeg
- */
-public class JcrNodeIteratorTest {
-
- private AbstractJcrNode node;
- @Mock
- private JcrSession session;
- private List<Location> children;
-
- @Before
- public void before() throws Exception {
- MockitoAnnotations.initMocks(this);
- ExecutionContext context = new ExecutionContext();
- stub(session.getExecutionContext()).toReturn(context);
- children = new ArrayList<Location>();
- node = new MockAbstractJcrNode(session, "node", null);
- }
-
- @Test
- public void shouldProvideNodeIterator() throws Exception {
- Node child1 = AbstractJcrNodeTest.createChild(session, "child1", 1, children, node);
- Node child2_1 = AbstractJcrNodeTest.createChild(session, "child2", 1, children, node);
- Node child2_2 = AbstractJcrNodeTest.createChild(session, "child2", 2, children, node);
- AbstractJcrNodeTest.createChild(session, "child3", 1, children, node);
- AbstractJcrNodeTest.createChild(session, "child4", 1, children, node);
- Node child5 = AbstractJcrNodeTest.createChild(session, "child5", 1, children, node);
- node.setChildren(children);
- NodeIterator iter = node.getNodes();
- assertThat(iter, notNullValue());
- assertThat(iter.getSize(), is(6L));
- assertThat(iter.getPosition(), is(0L));
- assertThat(iter.hasNext(), is(true));
- assertThat((Node)iter.next(), is(child1));
- assertThat(iter.getPosition(), is(1L));
- assertThat(iter.hasNext(), is(true));
- assertThat(iter.nextNode(), is(child2_1));
- assertThat(iter.getPosition(), is(2L));
- assertThat(iter.hasNext(), is(true));
- assertThat(iter.nextNode(), is(child2_2));
- assertThat(iter.getPosition(), is(3L));
- assertThat(iter.hasNext(), is(true));
- iter.skip(2);
- assertThat(iter.getPosition(), is(5L));
- assertThat(iter.hasNext(), is(true));
- assertThat(iter.nextNode(), is(child5));
- assertThat(iter.getPosition(), is(6L));
- assertThat(iter.hasNext(), is(false));
- }
-
- @Test( expected = UnsupportedOperationException.class )
- public void shouldNotAllowNodeIteratorRemove() throws Exception {
- node.getNodes().remove();
- }
-
- @Test( expected = IllegalArgumentException.class )
- public void shouldNotAllowNodeIteratorNegativeSkip() throws Exception {
- node.getNodes().skip(-1);
- }
-}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrNodeTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -25,17 +25,14 @@
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.UUID;
import javax.jcr.ItemNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.nodetype.NodeDefinition;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -46,56 +43,62 @@
*/
public class JcrNodeTest {
+ private UUID uuid;
private JcrNode node;
- private AbstractJcrNode root;
+ private ExecutionContext context;
@Mock
- private JcrSession session;
- @Mock
- private NodeDefinition rootNodeDefinition;
- @Mock
- private NodeDefinition nodeDefinition;
+ private SessionCache cache;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
- ExecutionContext context = new ExecutionContext();
- UUID rootUuid = UUID.randomUUID();
- Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
- Location rootLocation = Location.create(rootPath, rootUuid);
- root = new JcrRootNode(session, rootLocation, rootNodeDefinition);
- UUID uuid = UUID.randomUUID();
- Path path = context.getValueFactories().getPathFactory().create("/name[2]");
- Location location = Location.create(path, uuid);
- node = new JcrNode(session, rootUuid, location, nodeDefinition);
- stub(session.getExecutionContext()).toReturn(context);
- stub(session.getNode(rootUuid)).toReturn(root);
- stub(session.getNode(uuid)).toReturn(node);
- node.setProperties(new HashMap<Name, Property>());
- node.setChildren(new ArrayList<Location>());
+
+ uuid = UUID.randomUUID();
+ node = new JcrNode(cache, uuid);
+
+ context = new ExecutionContext();
+ stub(cache.context()).toReturn(context);
}
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
@Test( expected = ItemNotFoundException.class )
public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
- node.getAncestor(2);
+ NodeInfo info = mock(NodeInfo.class);
+ stub(cache.findNodeInfo(uuid)).toReturn(info);
+ stub(cache.getPathFor(info)).toReturn(path("/a/b/c/name[2]"));
+ node.getAncestor(6);
}
@Test
public void shouldProvideDepth() throws Exception {
- assertThat(node.getDepth(), is(1));
+ NodeInfo info = mock(NodeInfo.class);
+ stub(cache.findNodeInfo(uuid)).toReturn(info);
+ stub(cache.getPathFor(info)).toReturn(path("/a/b/c/name[2]"));
+ assertThat(node.getDepth(), is(4));
}
@Test
public void shouldProvideIndex() throws Exception {
- assertThat(node.getIndex(), is(2));
+ stub(cache.getSnsIndexOf(uuid)).toReturn(1);
+ assertThat(node.getIndex(), is(1));
}
@Test
public void shouldProvideName() throws Exception {
+ stub(cache.getNameOf(uuid)).toReturn(name("name"));
assertThat(node.getName(), is("name"));
}
@Test
public void shouldProvidePath() throws Exception {
- assertThat(node.getPath(), is("/name[2]"));
+ stub(cache.getPathFor(uuid)).toReturn(path("/a/b/c/name[2]"));
+ assertThat(node.getPath(), is("/a/b/c/name[2]"));
}
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrPropertyIteratorTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -32,31 +32,30 @@
import javax.jcr.PropertyIterator;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.jcr.AbstractJcrNodeTest.MockAbstractJcrNode;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoAnnotations.Mock;
/**
* @author jverhaeg
*/
public class JcrPropertyIteratorTest {
- private AbstractJcrNode node;
- @Mock
- private JcrSession session;
private Map<Name, Property> properties;
private ExecutionContext context;
+ private PropertyIterator iter;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
context = new ExecutionContext();
properties = new HashMap<Name, Property>();
- node = new MockAbstractJcrNode(session, "node", null);
- node.setProperties(properties);
+ properties.put(name("prop1"), Mockito.mock(Property.class));
+ properties.put(name("prop2"), Mockito.mock(Property.class));
+ properties.put(name("prop3"), Mockito.mock(Property.class));
+ properties.put(name("prop4"), Mockito.mock(Property.class));
+ iter = new JcrPropertyIterator(properties.values());
}
protected Name name( String name ) {
@@ -65,11 +64,6 @@
@Test
public void shouldProvidePropertyIterator() throws Exception {
- properties.put(name("prop1"), Mockito.mock(Property.class));
- properties.put(name("prop2"), Mockito.mock(Property.class));
- properties.put(name("prop3"), Mockito.mock(Property.class));
- properties.put(name("prop4"), Mockito.mock(Property.class));
- PropertyIterator iter = node.getProperties();
assertThat(iter, notNullValue());
assertThat(iter.getSize(), is(4L));
assertThat(iter.getPosition(), is(0L));
@@ -87,11 +81,11 @@
@Test( expected = UnsupportedOperationException.class )
public void shouldNotAllowPropertyIteratorRemove() throws Exception {
- node.getProperties().remove();
+ new JcrPropertyIterator(properties.values()).remove();
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowPropertyIteratorNegativeSkip() throws Exception {
- node.getProperties().skip(-1);
+ new JcrPropertyIterator(properties.values()).skip(-1);
}
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrRootNodeTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -26,14 +26,10 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
-import java.util.HashMap;
-import java.util.Map;
+import static org.mockito.Mockito.stub;
import java.util.UUID;
import javax.jcr.ItemNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.nodetype.NodeDefinition;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
import org.junit.Before;
@@ -46,25 +42,31 @@
*/
public class JcrRootNodeTest {
+ private UUID uuid;
private JcrRootNode root;
+ private ExecutionContext context;
@Mock
- private JcrSession session;
- @Mock
- private NodeDefinition nodeDefinition;
- private Map<Name, Property> properties;
+ private SessionCache cache;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
- properties = new HashMap<Name, Property>();
- ExecutionContext context = new ExecutionContext();
- UUID rootUuid = UUID.randomUUID();
- Path rootPath = context.getValueFactories().getPathFactory().createRootPath();
- Location rootLocation = Location.create(rootPath, rootUuid);
- root = new JcrRootNode(session, rootLocation, nodeDefinition);
- root.setProperties(properties);
+
+ uuid = UUID.randomUUID();
+ root = new JcrRootNode(cache, uuid);
+
+ context = new ExecutionContext();
+ stub(cache.context()).toReturn(context);
}
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
@Test( expected = ItemNotFoundException.class )
public void shouldNotAllowAncestorDepthGreaterThanNodeDepth() throws Exception {
root.getAncestor(1);
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -23,13 +23,13 @@
*/
package org.jboss.dna.jcr;
-import static org.junit.Assert.assertTrue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import java.io.ByteArrayOutputStream;
@@ -71,7 +71,7 @@
public class JcrSessionTest {
private static final String MULTI_LINE_VALUE = "Line\t1\nLine 2\rLine 3\r\nLine 4";
-
+
private String workspaceName;
private ExecutionContext context;
private InMemoryRepositorySource source;
@@ -102,7 +102,7 @@
graph.set("booleanProperty").on("/a/b").to(true);
graph.set("stringProperty").on("/a/b/c").to("value");
graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
-
+
// Make sure the path to the namespaces exists ...
graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
@@ -287,13 +287,10 @@
@Test
public void shouldProvideRootNode() throws Exception {
- Map<UUID, AbstractJcrNode> nodesByUuid = session.getNodesByUuid();
- assertThat(nodesByUuid.isEmpty(), is(true));
Node root = session.getRootNode();
assertThat(root, notNullValue());
UUID uuid = ((JcrRootNode)root).internalUuid();
assertThat(uuid, notNullValue());
- assertThat(nodesByUuid.get(uuid), is(root));
}
@Test
@@ -394,14 +391,14 @@
assertThat(session.getNamespaceURI("sv"), is("http://www.jcp.org/jcr/sv/1.0"));
// assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
}
-
+
@Test
public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
OutputStream os = new ByteArrayOutputStream();
-
+
session.exportSystemView("/a/b/c", os, false, true);
-
+
String fileContents = os.toString();
- assertTrue(fileContents.contains(MULTI_LINE_VALUE));
+ assertTrue(fileContents.contains(MULTI_LINE_VALUE));
}
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSingleValuePropertyTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -31,17 +31,15 @@
import java.io.InputStream;
import java.util.UUID;
import javax.jcr.Node;
-import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
-import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.PropertyDefinition;
import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.DateTime;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -52,39 +50,57 @@
*/
public class JcrSingleValuePropertyTest {
- private Property prop;
- private AbstractJcrNode node;
+ private PropertyId propertyId;
+ private JcrSingleValueProperty prop;
private ExecutionContext executionContext;
+ private org.jboss.dna.graph.property.Property dnaProperty;
@Mock
+ private SessionCache cache;
+ @Mock
private JcrSession session;
@Mock
- private NodeDefinition nodeDefinition;
+ private PropertyInfo propertyInfo;
@Mock
- Name name;
+ private JcrPropertyDefinition definition;
@Mock
- private PropertyDefinition definition;
- private org.jboss.dna.graph.property.Property dnaProperty;
+ private JcrNodeTypeManager nodeTypes;
@Before
- public void before() {
+ public void before() throws Exception {
MockitoAnnotations.initMocks(this);
executionContext = new ExecutionContext();
-
- UUID rootUuid = UUID.randomUUID();
- Path rootPath = executionContext.getValueFactories().getPathFactory().createRootPath();
- Location rootLocation = Location.create(rootPath, rootUuid);
- node = new JcrRootNode(session, rootLocation, nodeDefinition);
+ stub(cache.session()).toReturn(session);
+ stub(cache.context()).toReturn(executionContext);
+ stub(session.nodeTypeManager()).toReturn(nodeTypes);
stub(session.getExecutionContext()).toReturn(executionContext);
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "text/plain");
+ stub(definition.getRequiredType()).toReturn(PropertyType.STRING);
stub(definition.isMultiple()).toReturn(false);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+ PropertyDefinitionId definitionId = new PropertyDefinitionId(name("nodeTypeName"), name("propDefnName"));
+ stub(nodeTypes.getPropertyDefinition(definitionId, false)).toReturn(definition);
+
+ UUID uuid = UUID.randomUUID();
+ propertyId = new PropertyId(uuid, JcrLexicon.MIMETYPE);
+ prop = new JcrSingleValueProperty(cache, propertyId);
+
+ stub(cache.findPropertyInfo(propertyId)).toReturn(propertyInfo);
+ stub(propertyInfo.getDefinitionId()).toReturn(definitionId);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
+ stub(propertyInfo.isMultiValued()).toReturn(false);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyName()).toReturn(dnaProperty.getName());
}
+ protected Name name( String name ) {
+ return executionContext.getValueFactories().getNameFactory().create(name);
+ }
+
@Test
public void shouldProvideBoolean() throws Exception {
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BOOLEAN);
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "true");
- prop = new JcrSingleValueProperty(node, definition, PropertyType.BOOLEAN, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
assertThat(prop.getBoolean(), is(true));
assertThat(prop.getType(), is(PropertyType.BOOLEAN));
}
@@ -92,7 +108,6 @@
@Test
public void shouldIndicateHasSingleValue() throws Exception {
PropertyDefinition def = prop.getDefinition();
- assertThat(def, notNullValue());
assertThat(def.isMultiple(), is(false));
}
@@ -100,7 +115,8 @@
public void shouldProvideDate() throws Exception {
DateTime dnaDate = executionContext.getValueFactories().getDateFactory().create();
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaDate);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.DATE, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.DATE);
assertThat(prop.getDate(), is(dnaDate.toCalendar()));
assertThat(prop.getLong(), is(dnaDate.getMilliseconds()));
assertThat(prop.getType(), is(PropertyType.DATE));
@@ -111,9 +127,10 @@
public void shouldProvideNode() throws Exception {
UUID referencedUuid = UUID.randomUUID();
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, referencedUuid);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.REFERENCE);
AbstractJcrNode referencedNode = mock(AbstractJcrNode.class);
- stub(session.getNode(referencedUuid)).toReturn(referencedNode);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.REFERENCE, dnaProperty);
+ stub(cache.findJcrNode(referencedUuid)).toReturn(referencedNode);
assertThat(prop.getNode(), is((Node)referencedNode));
assertThat(prop.getType(), is(PropertyType.REFERENCE));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -122,12 +139,13 @@
@Test
public void shouldProvideDouble() throws Exception {
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.DOUBLE);
assertThat(prop.getDouble(), is(1.0));
assertThat(prop.getType(), is(PropertyType.DOUBLE));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1.0F);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
assertThat(prop.getDouble(), is(1.0));
assertThat(prop.getType(), is(PropertyType.DOUBLE));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -136,23 +154,26 @@
@Test
public void shouldProvideLong() throws Exception {
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.LONG);
assertThat(prop.getLong(), is(1L));
- assertThat(prop.getType(), is(PropertyType.DOUBLE));
+ assertThat(prop.getType(), is(PropertyType.LONG));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, 1L);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.DOUBLE, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.LONG);
assertThat(prop.getLong(), is(1L));
assertThat(prop.getString(), is("1"));
- assertThat(prop.getType(), is(PropertyType.DOUBLE));
+ assertThat(prop.getType(), is(PropertyType.LONG));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
}
@Test
public void shouldProvideStream() throws Exception {
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, new Object());
- prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.BINARY);
assertThat(prop.getType(), is(PropertyType.BINARY));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
InputStream stream = prop.getStream();
@@ -168,7 +189,8 @@
@Test
public void shouldProvideString() throws Exception {
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, "value");
- prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
assertThat(prop.getString(), is("value"));
assertThat(prop.getType(), is(PropertyType.STRING));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -178,7 +200,8 @@
public void shouldAllowReferenceValue() throws Exception {
UUID uuid = UUID.randomUUID();
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, uuid);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.STRING);
assertThat(prop.getString(), is(uuid.toString()));
assertThat(prop.getType(), is(PropertyType.STRING));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -189,7 +212,8 @@
executionContext.getNamespaceRegistry().register("acme", "http://example.com");
Name path = executionContext.getValueFactories().getNameFactory().create("acme:something");
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, path);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.NAME, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.NAME);
assertThat(prop.getString(), is("acme:something"));
assertThat(prop.getType(), is(PropertyType.NAME));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -203,7 +227,8 @@
executionContext.getNamespaceRegistry().register("acme", "http://example.com");
Path path = executionContext.getValueFactories().getPathFactory().create("/a/b/acme:c");
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, (Object)path);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.PATH, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
+ stub(propertyInfo.getPropertyType()).toReturn(PropertyType.PATH);
assertThat(prop.getString(), is("/a/b/acme:c"));
assertThat(prop.getType(), is(PropertyType.PATH));
assertThat(prop.getName(), is(dnaProperty.getName().getString(executionContext.getNamespaceRegistry())));
@@ -230,13 +255,13 @@
dnaValue = "some other value";
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, dnaValue);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.STRING, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
assertThat(prop.getLength(), is((long)dnaValue.length()));
Object obj = new Object();
long binaryLength = executionContext.getValueFactories().getBinaryFactory().create(obj).getSize();
dnaProperty = executionContext.getPropertyFactory().create(JcrLexicon.MIMETYPE, obj);
- prop = new JcrSingleValueProperty(node, definition, PropertyType.BINARY, dnaProperty);
+ stub(propertyInfo.getProperty()).toReturn(dnaProperty);
assertThat(prop.getLength(), is(binaryLength));
}
Added: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java (rev 0)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,480 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.jcr;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.stub;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.jcr.nodetype.NodeType;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.jcr.SessionCache.Children;
+import org.jboss.dna.jcr.SessionCache.NodeInfo;
+import org.jboss.dna.jcr.SessionCache.PropertyInfo;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ */
+public class SessionCacheTest {
+
+ private String workspaceName;
+ private ExecutionContext context;
+ private JcrNodeTypeManager nodeTypes;
+ private Stopwatch sw;
+ private Graph store;
+ private SessionCache cache;
+ private Map<String, Graph> storesByName;
+ private Map<String, SessionCache> cachesByName;
+
+ @Mock
+ private JcrSession session;
+
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ sw = new Stopwatch();
+ storesByName = new HashMap<String, Graph>();
+ cachesByName = new HashMap<String, SessionCache>();
+
+ context = new ExecutionContext();
+ context.getNamespaceRegistry().register("vehix", "http://example.com/vehicles");
+
+ workspaceName = "theWorkspace";
+
+ stub(session.getExecutionContext()).toReturn(context);
+ stub(session.namespaces()).toReturn(context.getNamespaceRegistry());
+
+ // Load up all the node types ...
+ JcrNodeTypeSource nodeTypeSource = new JcrBuiltinNodeTypeSource(session);
+ nodeTypeSource = new DnaBuiltinNodeTypeSource(session, nodeTypeSource);
+ nodeTypeSource = new VehixNodeTypeSource(session, nodeTypeSource);
+ nodeTypes = new JcrNodeTypeManager(session, nodeTypeSource);
+ stub(session.nodeTypeManager()).toReturn(nodeTypes);
+
+ InMemoryRepositorySource source = new InMemoryRepositorySource();
+ source.setName("store");
+ source.setDefaultWorkspaceName(workspaceName);
+ store = Graph.create(source, context);
+
+ // Import the "cars.xml" file into the repository
+ store.importXmlFrom(new File("src/test/resources/vehicles.xml")).into("/");
+
+ cache = new SessionCache(session, workspaceName, context, nodeTypes, store);
+ }
+
+ /**
+ * Define the node types for the "vehix" namespace.
+ */
+ public static class VehixNodeTypeSource extends AbstractJcrNodeTypeSource {
+ private final List<JcrNodeType> primaryNodeTypes;
+ private final List<JcrNodeType> mixinNodeTypes;
+
+ public VehixNodeTypeSource( JcrSession session,
+ JcrNodeTypeSource predecessor ) {
+ super(predecessor);
+ this.primaryNodeTypes = new ArrayList<JcrNodeType>();
+ this.mixinNodeTypes = new ArrayList<JcrNodeType>();
+ Name carName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:car");
+ Name aircraftName = session.getExecutionContext().getValueFactories().getNameFactory().create("vehix:aircraft");
+ JcrNodeType unstructured = findType(JcrNtLexicon.UNSTRUCTURED);
+
+ // Add in the "vehix:car" node type (which extends "nt:unstructured") ...
+ JcrNodeType car = new JcrNodeType(session, carName, Arrays.asList(new NodeType[] {unstructured}),
+ NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
+ ORDERABLE_CHILD_NODES);
+
+ // Add in the "vehix:aircraft" node type (which extends "nt:unstructured") ...
+ JcrNodeType aircraft = new JcrNodeType(session, aircraftName, Arrays.asList(new NodeType[] {unstructured}),
+ NO_PRIMARY_ITEM_NAME, NO_CHILD_NODES, NO_PROPERTIES, NOT_MIXIN,
+ ORDERABLE_CHILD_NODES);
+
+ primaryNodeTypes.addAll(Arrays.asList(new JcrNodeType[] {car, aircraft,}));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredMixinNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredMixinNodeTypes() {
+ return mixinNodeTypes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.jcr.AbstractJcrNodeTypeSource#getDeclaredPrimaryNodeTypes()
+ */
+ @Override
+ public Collection<JcrNodeType> getDeclaredPrimaryNodeTypes() {
+ return primaryNodeTypes;
+ }
+
+ }
+
+ protected Graph createFrom( String repositoryName,
+ String workspaceName,
+ File file ) throws IOException, SAXException {
+ InMemoryRepositorySource source = new InMemoryRepositorySource();
+ source.setName(repositoryName);
+ source.setDefaultWorkspaceName(workspaceName);
+ Graph graph = Graph.create(source, context);
+
+ if (file != null) {
+ graph.importXmlFrom(file).into("/");
+ }
+ return graph;
+ }
+
+ protected Graph getGraph( String name ) throws IOException, SAXException {
+ Graph graph = storesByName.get(name);
+ if (graph == null) {
+ graph = createFrom(name, name + " workspace", new File("src/test/resources/" + name + ".xml"));
+ storesByName.put(name, graph);
+ }
+ return graph;
+ }
+
+ protected SessionCache getCache( String name ) throws IOException, SAXException {
+ SessionCache cache = cachesByName.get(name);
+ if (cache == null) {
+ cache = new SessionCache(session, name + " workspace", context, nodeTypes, getGraph(name));
+ cachesByName.put(name, cache);
+ }
+ return cache;
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Path.Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ protected String pad( String name ) {
+ return StringUtil.justifyLeft(name, 22, ' ');
+ }
+
+ protected void assertSameProperties( NodeInfo nodeInfo,
+ Node dnaNode ) {
+ UUID uuid = dnaNode.getLocation().getUuid();
+ assertThat(nodeInfo.getUuid(), is(uuid));
+ assertThat(nodeInfo.getOriginalLocation().getUuid(), is(uuid));
+ Set<Name> propertyNames = nodeInfo.getProperties().keySet();
+ for (Name propertyName : propertyNames) {
+ PropertyInfo info = nodeInfo.getProperty(propertyName);
+ assertThat(info.getNodeUuid(), is(uuid));
+ assertThat(info.getPropertyId().getNodeId(), is(uuid));
+ assertThat(info.getPropertyId().getPropertyName(), is(propertyName));
+ assertThat(info.getProperty().getName(), is(propertyName));
+ Property actual = dnaNode.getProperty(propertyName);
+ if (actual != null) {
+ assertThat(info.getProperty().size(), is(actual.size()));
+ assertThat(info.getProperty().getValuesAsArray(), is(actual.getValuesAsArray()));
+ } else {
+ assertThat(propertyName, is(JcrLexicon.PRIMARY_TYPE));
+ }
+ }
+ }
+
+ @Test
+ public void shouldCreateWithValidParameters() {
+ assertThat(cache, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldFindNodeInfoForRootByUuid() throws Exception {
+ Node root = store.getNodeAt("/");
+ UUID rootUuid = root.getLocation().getUuid();
+ NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+ assertThat(rootInfo, is(notNullValue()));
+ assertThat(rootInfo.getUuid(), is(rootUuid));
+ assertThat(rootInfo.getDefinitionId().getNodeTypeName(), is(name("dna:root")));
+ assertThat(rootInfo.getDefinitionId().getChildDefinitionName(), is(name("*")));
+ assertThat(rootInfo.getPrimaryTypeName(), is(name("dna:root")));
+ assertThat(rootInfo.getOriginalLocation().getPath().isRoot(), is(true));
+ assertThat(rootInfo.getOriginalLocation().getUuid(), is(rootUuid));
+ Set<Name> rootProperties = rootInfo.getProperties().keySet();
+ assertThat(rootProperties, hasItems(name("jcr:primaryType")));
+ assertSameProperties(rootInfo, root);
+
+ PropertyInfo info = rootInfo.getProperty(JcrLexicon.PRIMARY_TYPE);
+ assertThat(info.getProperty().getFirstValue(), is((Object)name("dna:root")));
+ }
+
+ @Test
+ public void shouldRepeatedlyFindRootNodeInfoByUuid() throws Exception {
+ Node root = store.getNodeAt("/");
+ UUID rootUuid = root.getLocation().getUuid();
+ for (int i = 0; i != 20; ++i) {
+ NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+ assertThat(rootInfo, is(notNullValue()));
+ }
+ }
+
+ @Test
+ public void shouldRepeatedlyFindRootNodeInfoByPath() throws Exception {
+ String sourceName = "cars";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+ Node root = store.getNodeAt("/");
+ UUID rootUuid = root.getLocation().getUuid();
+ NodeInfo nodeInfo = cache.findNodeInfoForRoot();
+ assertThat(nodeInfo.getUuid(), is(rootUuid));
+ for (int i = 0; i != 20; ++i) {
+ sw.start();
+ assertThat(nodeInfo, is(sameInstance(cache.findNodeInfoForRoot())));
+ sw.stop();
+ assertThat(nodeInfo, is(sameInstance(cache.findNodeInfo(rootUuid))));
+ }
+ System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+ }
+
+ @Test
+ public void shouldRepeatedlyFindRootNodeInfoByUuidFromVehiclesSource() throws Exception {
+ String sourceName = "vehicles";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+ sw.start();
+ Node root = store.getNodeAt("/");
+ sw.stop();
+ UUID rootUuid = root.getLocation().getUuid();
+ for (int i = 0; i != 20; ++i) {
+ sw.start();
+ NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+ sw.stop();
+ assertThat(rootInfo, is(notNullValue()));
+ }
+ System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+ }
+
+ @Test
+ public void shouldRepeatedlyFindRootNodeInfoByUuidFromSourceWithPrimaryTypes() throws Exception {
+ String sourceName = "repositoryForTckTests";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+ sw.start();
+ Node root = store.getNodeAt("/");
+ sw.stop();
+ UUID rootUuid = root.getLocation().getUuid();
+ for (int i = 0; i != 20; ++i) {
+ sw.start();
+ NodeInfo rootInfo = cache.findNodeInfo(rootUuid);
+ sw.stop();
+ assertThat(rootInfo, is(notNullValue()));
+ }
+ System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+ }
+
+ @Test
+ public void shouldFindChildrenInNodeInfoForRoot() throws Exception {
+ NodeInfo root = cache.findNodeInfoForRoot();
+ Children children = root.getChildren();
+ assertThat(children, is(notNullValue()));
+ assertThat(children.size(), is(1));
+ assertThat(children.getChild(segment("vehix:Vehicles")), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldFindNodeInfoForNonRootNodeByPath() throws Exception {
+ String sourceName = "vehicles";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+ // Get the root ...
+ NodeInfo root = cache.findNodeInfoForRoot();
+
+ // Now try to load a node that is well-below the root ...
+ Path lr3Path = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ Node lr3Node = store.getNodeAt(lr3Path);
+ sw.start();
+ NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), lr3Path);
+ sw.stop();
+ assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+ assertSameProperties(lr3, lr3Node);
+ System.out.println(pad(sourceName) + " ==> " + sw.getSimpleStatistics());
+
+ // Verify that this loaded all the intermediate nodes, by walking up ...
+ NodeInfo info = lr3;
+ while (true) {
+ UUID parent = info.getParent();
+ if (parent == null) {
+ // then we should be at the root ...
+ assertThat(info.getUuid(), is(root.getUuid()));
+ break;
+ }
+ // Otherwise, we're not at the root, so we should find the parent ...
+ info = cache.findNodeInfoInCache(parent);
+ assertThat(info, is(notNullValue()));
+ }
+ }
+
+ @Test
+ public void shouldFindInfoForNodeUsingRelativePathFromRoot() throws Exception {
+ String sourceName = "vehicles";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+
+ // Verify that the node does exist in the source ...
+ Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+
+ // Get the root ...
+ NodeInfo root = cache.findNodeInfoForRoot();
+
+ // Now try to load a node that is well-below the root ...
+ Path lr3Path = path("vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ assertThat(lr3Path.isAbsolute(), is(false));
+ NodeInfo lr3 = cache.findNodeInfo(root.getUuid(), lr3Path);
+ assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+ assertSameProperties(lr3, lr3Node);
+
+ Path recoveredPath = cache.getPathFor(lr3);
+ assertThat(recoveredPath, is(lr3AbsolutePath));
+ }
+
+ @Test
+ public void shouldFindInfoForNodeUsingRelativePathFromNonRoot() throws Exception {
+ String sourceName = "vehicles";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+
+ // Verify that the node does exist in the source ...
+ Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
+ Node carsNode = store.getNodeAt(carsAbsolutePath);
+ Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+ Path b787AbsolutePath = path("/vehix:Vehicles/vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+ Node b787Node = store.getNodeAt(b787AbsolutePath);
+
+ // Get the root ...
+ NodeInfo root = cache.findNodeInfoForRoot();
+
+ // Now try to load the cars node ...
+ Path carsPath = path("vehix:Vehicles/vehix:Cars");
+ assertThat(carsPath.isAbsolute(), is(false));
+ NodeInfo cars = cache.findNodeInfo(root.getUuid(), carsPath);
+ assertThat(cars.getUuid(), is(carsNode.getLocation().getUuid()));
+ assertSameProperties(cars, carsNode);
+
+ // Now try to find the LR3 node relative to the car ...
+ Path lr3Path = path("vehix:Utility/vehix:Land Rover LR3");
+ assertThat(lr3Path.isAbsolute(), is(false));
+ NodeInfo lr3 = cache.findNodeInfo(cars.getUuid(), lr3Path);
+ assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+ assertSameProperties(lr3, lr3Node);
+
+ // Now try to find the "Boeing 787" node relative to the LR3 node ...
+ Path b787Path = path("../../../vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+ assertThat(b787Path.isAbsolute(), is(false));
+ assertThat(b787Path.isNormalized(), is(true));
+ NodeInfo b787 = cache.findNodeInfo(lr3.getUuid(), b787Path);
+ assertThat(b787.getUuid(), is(b787Node.getLocation().getUuid()));
+ assertSameProperties(b787, b787Node);
+
+ assertThat(cache.getPathFor(cars), is(carsAbsolutePath));
+ assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
+ assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
+ }
+
+ @Test
+ public void shouldFindJcrNodeUsingAbsolutePaths() throws Exception {
+ String sourceName = "vehicles";
+ Graph store = getGraph(sourceName);
+ SessionCache cache = getCache(sourceName);
+
+ // Verify that the node does exist in the source ...
+ Path carsAbsolutePath = path("/vehix:Vehicles/vehix:Cars");
+ Node carsNode = store.getNodeAt(carsAbsolutePath);
+ Path lr3AbsolutePath = path("/vehix:Vehicles/vehix:Cars/vehix:Utility/vehix:Land Rover LR3");
+ Node lr3Node = store.getNodeAt(lr3AbsolutePath);
+ Path b787AbsolutePath = path("/vehix:Vehicles/vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+ Node b787Node = store.getNodeAt(b787AbsolutePath);
+
+ // Get the root ...
+ NodeInfo root = cache.findNodeInfoForRoot();
+
+ // Now try to load the cars node ...
+ Path carsPath = path("vehix:Vehicles/vehix:Cars");
+ assertThat(carsPath.isAbsolute(), is(false));
+ AbstractJcrNode carJcrNode = cache.findJcrNode(root.getUuid(), carsAbsolutePath);
+ assertThat(carJcrNode.internalUuid(), is(carsNode.getLocation().getUuid()));
+ NodeInfo cars = cache.findNodeInfo(root.getUuid(), carsPath);
+ assertThat(cars.getUuid(), is(carsNode.getLocation().getUuid()));
+ assertSameProperties(cars, carsNode);
+
+ // Now try to find the LR3 node relative to the car ...
+ Path lr3Path = path("vehix:Utility/vehix:Land Rover LR3");
+ assertThat(lr3Path.isAbsolute(), is(false));
+ AbstractJcrNode lr3JcrNode = cache.findJcrNode(root.getUuid(), lr3AbsolutePath);
+ assertThat(lr3JcrNode.internalUuid(), is(lr3Node.getLocation().getUuid()));
+ NodeInfo lr3 = cache.findNodeInfo(cars.getUuid(), lr3Path);
+ assertThat(lr3.getUuid(), is(lr3Node.getLocation().getUuid()));
+ assertSameProperties(lr3, lr3Node);
+
+ // Now try to find the "Boeing 787" node relative to the LR3 node ...
+ Path b787Path = path("../../../vehix:Aircraft/vehix:Commercial/vehix:Boeing 787");
+ assertThat(b787Path.isAbsolute(), is(false));
+ assertThat(b787Path.isNormalized(), is(true));
+ AbstractJcrNode b787JcrNode = cache.findJcrNode(root.getUuid(), b787AbsolutePath);
+ assertThat(b787JcrNode.internalUuid(), is(b787Node.getLocation().getUuid()));
+ NodeInfo b787 = cache.findNodeInfo(lr3.getUuid(), b787Path);
+ assertThat(b787.getUuid(), is(b787Node.getLocation().getUuid()));
+ assertSameProperties(b787, b787Node);
+
+ assertThat(cache.getPathFor(cars), is(carsAbsolutePath));
+ assertThat(cache.getPathFor(lr3), is(lr3AbsolutePath));
+ assertThat(cache.getPathFor(b787), is(b787AbsolutePath));
+ }
+}
Property changes on: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/SessionCacheTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-jcr/src/test/resources/cars.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/cars.xml 2009-03-12 20:54:55 UTC (rev 776)
+++ trunk/dna-jcr/src/test/resources/cars.xml 2009-03-16 22:50:09 UTC (rev 777)
@@ -45,4 +45,4 @@
<car jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
<car jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
</Utility>
-</Cars>\ No newline at end of file
+</Cars>
\ No newline at end of file
Added: trunk/dna-jcr/src/test/resources/vehicles.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/vehicles.xml (rev 0)
+++ trunk/dna-jcr/src/test/resources/vehicles.xml 2009-03-16 22:50:09 UTC (rev 777)
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<Vehicles xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns="http://example.com/vehicles">
+ <Cars>
+ <Hybrid>
+ <car jcr:name="Toyota Prius" maker="Toyota" model="Prius" year="2008" msrp="$21,500" userRating="4.2" valueRating="5" mpgCity="48" mpgHighway="45"/>
+ <car jcr:name="Toyota Highlander" maker="Toyota" model="Highlander" year="2008" msrp="$34,200" userRating="4" valueRating="5" mpgCity="27" mpgHighway="25"/>
+ <car jcr:name="Nissan Altima" maker="Nissan" model="Altima" year="2008" msrp="$18,260" mpgCity="23" mpgHighway="32"/>
+ </Hybrid>
+ <Sports>
+ <car jcr:name="Aston Martin DB9" maker="Aston Martin" model="DB9" year="2008" msrp="$171,600" userRating="5" mpgCity="12" mpgHighway="19" lengthInInches="185.5" wheelbaseInInches="108.0" engine="5,935 cc 5.9 liters V 12"/>
+ <car jcr:name="Infiniti G37" maker="Infiniti" model="G37" year="2008" msrp="$34,900" userRating="3.5" valueRating="4" mpgCity="18" mpgHighway="24" />
+ </Sports>
+ <Luxury>
+ <car jcr:name="Cadillac DTS" maker="Cadillac" model="DTS" year="2008" engine="3.6-liter V6" userRating="0"/>
+ <car jcr:name="Bentley Continental" maker="Bentley" model="Continental" year="2008" msrp="$170,990" mpgCity="10" mpgHighway="17" />
+ <car jcr:name="Lexus IS350" maker="Lexus" model="IS350" year="2008" msrp="$36,305" mpgCity="18" mpgHighway="25" userRating="4" valueRating="5" />
+ </Luxury>
+ <Utility>
+ <car jcr:name="Land Rover LR2" maker="Land Rover" model="LR2" year="2008" msrp="$33,985" userRating="4.5" valueRating="5" mpgCity="16" mpgHighway="23" />
+ <car jcr:name="Land Rover LR3" maker="Land Rover" model="LR3" year="2008" msrp="$48,525" userRating="5" valueRating="2" mpgCity="12" mpgHighway="17" />
+ <car jcr:name="Hummer H3" maker="Hummer" model="H3" year="2008" msrp="$30,595" userRating="3.5" valueRating="4" mpgCity="13" mpgHighway="16" />
+ <car jcr:name="Ford F-150" maker="Ford" model="F-150" year="2008" msrp="$23,910" userRating="4" valueRating="1" mpgCity="14" mpgHighway="20" />
+ </Utility>
+ </Cars>
+ <Aircraft>
+ <Business>
+ <aircraft jcr:name="Gulfstream V" maker="Gulfstream" model="G-V" introduced="1995" range="5800nm" cruiseSpeed="488kt" crew="2" emptyWeight="46200lb" url="http://en.wikipedia.org/wiki/Gulfstream_V"/>
+ <aircraft jcr:name="Learjet 45" maker="Learjet" model="LJ45" introduced="1995" numberBuilt="264+" crew="2" emptyWeight="13695lb" range="2120nm" cruiseSpeed="457kt" url="http://en.wikipedia.org/wiki/Learjet_45"/>
+ </Business>
+ <Commercial>
+ <aircraft jcr:name="Boeing 777" maker="Boeing" model="777-200LR" introduced="1995" numberBuilt="731+" maxRange="7500nm" emptyWeight="326000lb" cruiseSpeed="560mph" url="http://en.wikipedia.org/wiki/Boeing_777"/>
+ <aircraft jcr:name="Boeing 767" maker="Boeing" model="767-200" introduced="1982" numberBuilt="966+" maxRange="3950nm" emptyWeight="176650lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_767"/>
+ <aircraft jcr:name="Boeing 787" maker="Boeing" model="787-3" introduced="2009" range="3050nm" emptyWeight="223000lb" cruiseSpeed="561mph" url="http://en.wikipedia.org/wiki/Boeing_787"/>
+ <aircraft jcr:name="Boeing 757" maker="Boeing" model="757-200" introduced="1983" numberBuilt="1050" range="3900nm" maxWeight="255000lb" cruiseSpeed="530mph" url="http://en.wikipedia.org/wiki/Boeing_757"/>
+ <aircraft jcr:name="Airbus A380" maker="Airbus" model="A380-800" introduced="2007" numberBuilt="18" range="8200nm" maxWeight="1235000lb" cruiseSpeed="647mph" url="http://en.wikipedia.org/wiki/Airbus_a380"/>
+ <aircraft jcr:name="Airbus A340" maker="Airbus" model="A340-200" introduced="1993" numberBuilt="354" range="8000nm" maxWeight="606300lb" cruiseSpeed="557mph" url="http://en.wikipedia.org/wiki/Airbus_A-340"/>
+ <aircraft jcr:name="Airbus A310" maker="Airbus" model="A310-200" introduced="1983" numberBuilt="255" cruiseSpeed="850km/h" emptyWeight="176312lb" range="3670nm" url="http://en.wikipedia.org/wiki/Airbus_A-310"/>
+ <aircraft jcr:name="Embraer RJ-175" maker="Embraer" model="ERJ170-200" introduced="2004" range="3334km" cruiseSpeed="481kt" emptyWeight="21810kg" url="http://en.wikipedia.org/wiki/EMBRAER_170"/>
+ </Commercial>
+ <Vintage>
+ <aircraft jcr:name="Fokker Trimotor" maker="Fokker" model="F.VII" introduced="1925" cruiseSpeed="170km/h" emptyWeight="3050kg" crew="2" url="http://en.wikipedia.org/wiki/Fokker_trimotor"/>
+ <aircraft jcr:name="P-38 Lightning" maker="Lockheed" model="P-38" designedBy="Kelly Johnson" introduced="1941" numberBuilt="10037" rateOfClimb="4750ft/min" range="1300mi" emptyWeight="12780lb" crew="1" url="http://en.wikipedia.org/wiki/P-38_Lightning"/>
+ <aircraft jcr:name="A6M Zero" maker="Mitsubishi" model="A6M" designedBy="Jiro Horikoshi" introduced="1940" numberBuilt="11000" crew="1" emptyWeight="3704lb" serviceCeiling="33000ft" maxSpeed="331mph" range="1929mi" rateOfClimb="3100ft/min" url="http://en.wikipedia.org/wiki/A6M_Zero"/>
+ <aircraft jcr:name="Bf 109" maker="Messerschmitt" model="Bf 109" introduced="1937" url="http://en.wikipedia.org/wiki/BF_109"/>
+ <aircraft jcr:name="Wright Flyer" maker="Wright Brothers" introduced="1903" range="852ft" maxSpeed="30mph" emptyWeight="605lb" crew="1"/>
+ </Vintage>
+ <Homebuilt>
+ <aircraft jcr:name="Long-EZ" maker="Rutan Aircraft Factory" model="61" emptyWeight="760lb" fuelCapacity="200L" maxSpeed="185kt" since="1976" range="1200nm" url="http://en.wikipedia.org/wiki/Rutan_Long-EZ"/>
+ <aircraft jcr:name="Cirrus VK-30" maker="Cirrus Design" model="VK-30" emptyWeight="2400lb" maxLoad="1200lb" maxSpeed="250mph" rateOfClimb="1500ft/min" range="1300mi" url="http://en.wikipedia.org/wiki/Cirrus_VK-30"/>
+ <aircraft jcr:name="Van's RV-4" maker="Van's Aircraft" model="RV-4" introduced="1980" emptyWeight="905lb" maxLoad="500lb" maxSpeed="200mph" rateOfClimb="2450ft/min" range="725mi" url="http://en.wikipedia.org/wiki/Van%27s_Aircraft_RV-4"/>
+ </Homebuilt>
+ </Aircraft>
+</Vehicles>
\ No newline at end of file
Property changes on: trunk/dna-jcr/src/test/resources/vehicles.xml
___________________________________________________________________
Name: svn:mime-type
+ text/plain
15 years, 1 month
DNA SVN: r776 - in trunk/dna-jcr/src: test/java/org/jboss/dna/jcr and 1 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-03-12 16:54:55 -0400 (Thu, 12 Mar 2009)
New Revision: 776
Modified:
trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
Log:
DNA-297 JcrSession.exportDocumentView Does Not Export Carriage Returns, Line Feeds, and Tabs Correctly
Applied the "redux" patch, and verified that all tests do pass.
Modified: trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java
===================================================================
--- trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java 2009-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/main/java/org/jboss/dna/jcr/JcrDocumentViewExporter.java 2009-03-12 20:54:55 UTC (rev 776)
@@ -24,7 +24,11 @@
package org.jboss.dna.jcr;
import java.io.OutputStream;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
@@ -33,6 +37,8 @@
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.text.XmlNameEncoder;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.ValueFactories;
@@ -50,6 +56,8 @@
@NotThreadSafe
class JcrDocumentViewExporter extends AbstractJcrExporter {
+ private static final TextEncoder VALUE_ENCODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
+
JcrDocumentViewExporter( JcrSession session ) {
super(session, Collections.<String>emptyList());
}
@@ -111,7 +119,7 @@
propName.getLocalName(),
localPropName,
PropertyType.nameFromValue(prop.getType()),
- value.getString());
+ VALUE_ENCODER.encode(value.getString()));
}
Name name;
@@ -211,4 +219,74 @@
return xmlCharacters.getValue().getString();
}
+ /**
+ * Special {@link TextEncoder} that implements the subset of XML name encoding suggested by section 6.4.4 of the JCR 1.0.1
+ * specification. This encoder only encodes space (0x20), carriage return (0x0D), new line (0x0A), tab (0x09), and any
+ * underscore characters that might otherwise suggest an encoding, as defined in {@link XmlNameEncoder}.
+ *
+ */
+ protected static class JcrDocumentViewPropertyEncoder extends XmlNameEncoder {
+ private static final Set<Character> MAPPED_CHARACTERS;
+
+ static {
+ MAPPED_CHARACTERS = new HashSet<Character>();
+ MAPPED_CHARACTERS.add(' ');
+ MAPPED_CHARACTERS.add('\r');
+ MAPPED_CHARACTERS.add('\n');
+ MAPPED_CHARACTERS.add('\t');
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.common.text.TextEncoder#encode(java.lang.String)
+ */
+ // See section 6.4.4 of the JCR 1.0.1 spec for why these hoops must be jumped through
+ @Override
+ public String encode( String text ) {
+ if (text == null) return null;
+ if (text.length() == 0) return text;
+ StringBuilder sb = new StringBuilder();
+ String hex = null;
+ CharacterIterator iter = new StringCharacterIterator(text);
+ for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
+ if (c == '_') {
+ // Read the next character (if there is one) ...
+ char next = iter.next();
+ if (next == CharacterIterator.DONE) {
+ sb.append(c);
+ break;
+ }
+ // If the next character is not 'x', then these are just regular characters ...
+ if (next != 'x') {
+ sb.append(c).append(next);
+ continue;
+ }
+ // The next character is 'x', so write out the '_' character in encoded form ...
+ sb.append("_x005f_");
+ // And then write out the next character ...
+ sb.append(next);
+ } else if (!MAPPED_CHARACTERS.contains(c)) {
+ // Legal characters for an XML Name ...
+ sb.append(c);
+ } else {
+ // All other characters must be escaped with '_xHHHH_' where 'HHHH' is the hex string for the code point
+ hex = Integer.toHexString(c);
+ // The hex string excludes the leading '0's, so check the character values so we know how many to prepend
+ if (c >= '\u0000' && c <= '\u000f') {
+ sb.append("_x000").append(hex);
+ } else if (c >= '\u0010' && c <= '\u00ff') {
+ sb.append("_x00").append(hex);
+ } else if (c >= '\u0100' && c <= '\u0fff') {
+ sb.append("_x0").append(hex);
+ } else {
+ sb.append("_x").append(hex);
+ }
+ sb.append('_');
+ }
+ }
+ return sb.toString();
+ }
+ }
}
Modified: trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java
===================================================================
--- trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/test/java/org/jboss/dna/jcr/JcrSessionTest.java 2009-03-12 20:54:55 UTC (rev 776)
@@ -1,5 +1,5 @@
/*
- * JBoss DNA (http://www.jboss.org/dna)
+* JBoss DNA (http://www.jboss.org/dna)
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
@@ -23,6 +23,7 @@
*/
package org.jboss.dna.jcr;
+import static org.junit.Assert.assertTrue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.IsNot.not;
@@ -31,7 +32,9 @@
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
+import java.io.ByteArrayOutputStream;
import java.io.InputStream;
+import java.io.OutputStream;
import java.security.AccessControlException;
import java.security.Principal;
import java.util.Calendar;
@@ -67,6 +70,8 @@
*/
public class JcrSessionTest {
+ private static final String MULTI_LINE_VALUE = "Line\t1\nLine 2\rLine 3\r\nLine 4";
+
private String workspaceName;
private ExecutionContext context;
private InMemoryRepositorySource source;
@@ -96,7 +101,8 @@
graph.create("/a").and().create("/a/b").and().create("/a/b/c");
graph.set("booleanProperty").on("/a/b").to(true);
graph.set("stringProperty").on("/a/b/c").to("value");
-
+ graph.set("multiLineProperty").on("/a/b/c").to(MULTI_LINE_VALUE);
+
// Make sure the path to the namespaces exists ...
graph.create("/jcr:system").and().create("/jcr:system/dna:namespaces");
@@ -388,4 +394,14 @@
assertThat(session.getNamespaceURI("sv"), is("http://www.jcp.org/jcr/sv/1.0"));
// assertThat(session.getNamespaceURI("xml"), is("http://www.w3.org/XML/1998/namespace"));
}
+
+ @Test
+ public void shouldExportMultiLinePropertiesInSystemView() throws Exception {
+ OutputStream os = new ByteArrayOutputStream();
+
+ session.exportSystemView("/a/b/c", os, false, true);
+
+ String fileContents = os.toString();
+ assertTrue(fileContents.contains(MULTI_LINE_VALUE));
+ }
}
Modified: trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml
===================================================================
--- trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml 2009-03-12 20:40:20 UTC (rev 775)
+++ trunk/dna-jcr/src/test/resources/repositoryForTckTests.xml 2009-03-12 20:54:55 UTC (rev 776)
@@ -25,14 +25,27 @@
~ Boston, MA 02110-1301 USA
-->
<testroot xmlns:jcr="http://www.jcp.org/jcr/1.0"
- xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
- xmlns:dna="http://www.jboss.org/dna/1.0"
- jcr:primaryType="nt:unstructured">
- <nt:unstructured jcr:name="node1" prop1="<foo&foo>" >
- <!-- This stanza checks for the jcr:xmltext special case for document export. DO NOT change this element. -->
- <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my "XML" text!" />
- </nt:unstructured>
- <nt:unstructured jcr:name="node2 has a multi-word name" prop2="bar" />
- <nt:unstructured jcr:name="node3" />
- <nt:unstructured jcr:name="node4" />
+ xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+ xmlns:dna="http://www.jboss.org/dna/1.0"
+ jcr:primaryType="nt:unstructured">
+ <nt:unstructured jcr:name="node1" prop1="<foo&foo>" >
+ <!--
+ This stanza checks for the jcr:xmltext special case for export as per JCR 1.0.1 section 6.4.2.3
+ -->
+ <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my "XML" text!" />
+ </nt:unstructured>
+ <nt:unstructured jcr:name="node2 has a multi-word name" multi-valued-property="bar baz blah">
+ <!--
+ This stanza checks that consecutive jcr:xmltext elements will be merged in the export as
+ per JCR 1.0.1 section 6.4.2.3
+ -->
+ <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my "XML" text!" />
+ <nt:unstructured jcr:name="jcr:xmltext" jcr:xmlcharacters="This is my "other" text!" />
+ </nt:unstructured>
+ <nt:unstructured jcr:name="node3" />
+ <!--
+ Test of unprintable character exports.
+ Cannot test \r character here on Windows as TCK XML Parser will replace it with \n and fail the comparison
+ -->
+ <nt:unstructured jcr:name="node4" multi-line-property="Line	1
Line 2"/>
</testroot>
\ No newline at end of file
15 years, 1 month