You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2007/09/21 16:14:24 UTC

svn commit: r578137 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: ItemImpl.java NodeImpl.java

Author: stefan
Date: Fri Sep 21 07:14:23 2007
New Revision: 578137

URL: http://svn.apache.org/viewvc?rev=578137&view=rev
Log:
JCR-1104: JSR 283 support

work in (slow) progress...

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java?rev=578137&r1=578136&r2=578137&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ItemImpl.java Fri Sep 21 07:14:23 2007
@@ -448,6 +448,7 @@
          *
          * for every transient item:
          * - if it is 'modified' check the WRITE permission
+         * - if it is 'removed' check the REMOVE permission
          *
          * for every transient node:
          * - if it is 'new' check that its node type satisfies the
@@ -460,7 +461,7 @@
          *
          * note that the protected flag is checked in Node.addNode/Node.remove
          * (for adding/removing child entries of a node), in
-         * Node.addMixin/removeMixin (for mixin changes on nodes)
+         * Node.addMixin/removeMixin/setPrimaryType (for type changes on nodes)
          * and in Property.setValue (for properties to be modified).
          */
 
@@ -495,11 +496,13 @@
                 // effective node type (primary type incl. mixins)
                 EffectiveNodeType ent = validator.getEffectiveNodeType(nodeState);
                 /**
-                 * if the transient node was added (i.e. if it is 'new'),
-                 * check its node's node type against the required node type
-                 * in its definition
+                 * if the transient node was added (i.e. if it is 'new') or if
+                 * its primary type has changed, check its node type against the
+                 * required node type in its definition
                  */
-                if (nodeState.getStatus() == ItemState.STATUS_NEW) {
+                if (nodeState.getStatus() == ItemState.STATUS_NEW
+                        || !nodeState.getNodeTypeName().equals(
+                            ((NodeState) nodeState.getOverlayedState()).getNodeTypeName())) {
                     NodeType[] nta = def.getRequiredPrimaryTypes();
                     for (int i = 0; i < nta.length; i++) {
                         NodeTypeImpl ntReq = (NodeTypeImpl) nta[i];

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=578137&r1=578136&r2=578137&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Fri Sep 21 07:14:23 2007
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.nodetype.PropDef;
 import org.apache.jackrabbit.core.nodetype.PropertyDefinitionImpl;
+import org.apache.jackrabbit.core.nodetype.ItemDef;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.NodeReferences;
@@ -4324,6 +4325,230 @@
             String msg = "Unable to retrieve REFERENCE properties that refer to " + id;
             log.debug(msg);
             throw new RepositoryException(msg, e);
+        }
+    }
+
+    /**
+     * Changes the primary node type of this node to <code>nodeTypeName</code>.
+     * Also immediately changes this node's <code>jcr:primaryType</code> property
+     * appropriately. Semantically, the new node type may take effect
+     * immediately and <i>must</i> take effect on <code>save</code>. Whichever
+     * behavior is adopted it must be the same as the behavior adopted for
+     * <code>addMixin()</code> (see below) and the behavior that occurs when a
+     * node is first created.
+     * <p/>
+     * If the presence of an existing property or child node would cause an
+     * incompatibility with the new node type a <code>ConstraintViolationException</code>
+     * is thrown either immediately or on <code>save</code>.
+     * <p/>
+     * If the new node type would cause this node to be incompatible with the
+     * node type of its parent then a <code>ConstraintViolationException</code>
+     * is thrown either immediately or on <code>save</code>.
+     * <p/>
+     * A <code>ConstraintViolationException</code> is also thrown either
+     * immediately or on <code>save</code> if a conflict with an already
+     * assigned mixin occurs.
+     * <p/>
+     * A <code>ConstraintViolationException</code> may also be thrown either
+     * immediately or on <code>save</code> if the attempted change violates
+     * implementation-specific node type transition rules. A repository that
+     * disallows all primary node type changes would simple throw this
+     * exception in all cases.
+     * <p/>
+     * If the specified node type is not recognized a
+     * <code>NoSuchNodeTypeException</code> is thrown either immediately
+     * or on <code>save</code>.
+     * <p/>
+     * A <code>VersionException</code> is thrown either immediately or on
+     * <code>save</code> if this node is versionable and checked-in, or is
+     * non-versionable but its nearest versionable ancestor is checked-in.
+     * <p/>
+     * A <code>LockException</code> is thrown either immediately or on
+     * <code>save</code> if a lock prevents the change of node type.
+     * <p/>
+     * A <code>RepositoryException</code> will be thrown if another error occurs.
+     *
+     * @param nodeTypeName the name of the new node type.
+     * @throws ConstraintViolationException If the specified primary node type
+     * is prevented from being assigned.
+     * @throws NoSuchNodeTypeException If the specified <code>nodeTypeName</code>
+     * is not recognized and this implementation performs this validation
+     * immediately instead of waiting until <code>save</code>.
+     * @throws VersionException if this node is versionable and checked-in or is
+     * non-versionable but its nearest versionable ancestor is checked-in and this
+     * implementation performs this validation immediately instead of waiting until
+     * <code>save</code>.
+     * @throws LockException if a lock prevents the change of the primary node type
+     * and this implementation performs this validation immediately instead of
+     * waiting until <code>save</code>.
+     * @throws RepositoryException  if another error occurs.
+     * @since JCR 2.0
+     */
+    public void setPrimaryType(String nodeTypeName)
+            throws NoSuchNodeTypeException, VersionException,
+            ConstraintViolationException, LockException, RepositoryException {
+        // check state of this instance
+        sanityCheck();
+
+        // make sure this node is checked-out
+        if (!internalIsCheckedOut()) {
+            String msg = safeGetJCRPath() + ": cannot set primary type of a checked-in node";
+            log.debug(msg);
+            throw new VersionException(msg);
+        }
+
+        // check protected flag
+        if (definition.isProtected()) {
+            String msg = safeGetJCRPath() + ": cannot set primary type of a protected node";
+            log.debug(msg);
+            throw new ConstraintViolationException(msg);
+        }
+
+        if (state.getParentId() == null) {
+            String msg = "changing the primary type of the root node is not supported";
+            log.debug(msg);
+            throw new RepositoryException(msg);
+        }
+
+        // check lock status
+        checkLock();
+
+        QName ntName;
+        try {
+            ntName = session.getQName(nodeTypeName);
+        } catch (NameException e) {
+            throw new RepositoryException(
+                    "invalid node type name: " + nodeTypeName, e);
+        }
+
+        if (ntName.equals(primaryTypeName)) {
+            return;
+        }
+
+        NodeTypeManagerImpl ntMgr = session.getNodeTypeManager();
+        if (ntMgr.getNodeType(ntName).isMixin()) {
+            throw new RepositoryException(nodeTypeName + ": not a primary node type");
+        }
+
+        // build effective node type of new primary type & existing mixin's
+        // in order to detect conflicts
+        NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
+        EffectiveNodeType entNew, entOld;
+        try {
+            entNew = ntReg.getEffectiveNodeType(ntName);
+            entOld = ntReg.getEffectiveNodeType(primaryTypeName);
+
+            // existing mixin's
+            HashSet set = new HashSet(((NodeState) state).getMixinTypeNames());
+            // new primary type
+            set.add(ntName);
+            // try to build new effective node type (will throw in case of conflicts)
+            ntReg.getEffectiveNodeType((QName[]) set.toArray(new QName[set.size()]));
+        } catch (NodeTypeConflictException ntce) {
+            throw new ConstraintViolationException(ntce.getMessage());
+        }
+
+        // get applicable definition for this node using new primary type
+        NodeDefId defId;
+        try {
+            NodeImpl parent = (NodeImpl) getParent();
+            defId = parent.getApplicableChildNodeDefinition(getQName(), ntName).unwrap().getId();
+        } catch (RepositoryException re) {
+            String msg = safeGetJCRPath() + ": no applicable definition found in parent node's node type";
+            log.debug(msg);
+            throw new ConstraintViolationException(msg, re);
+        }
+        
+        if (!defId.equals(((NodeState) state).getDefinitionId())) {
+            onRedefine(defId);
+        }
+
+
+        // build change set: removed/added child items
+        Set oldDefs = new HashSet(Arrays.asList(entOld.getAllItemDefs()));
+        Set newDefs = new HashSet(Arrays.asList(entNew.getAllItemDefs()));
+
+        Set removedDefs = new HashSet(oldDefs);
+        removedDefs.removeAll(newDefs);
+
+        Set addedDefs = new HashSet(newDefs);
+        addedDefs.removeAll(oldDefs);
+
+        // referential integrity check
+        boolean referenceableOld = entOld.includesNodeType(QName.MIX_REFERENCEABLE);
+        boolean referenceableNew = entNew.includesNodeType(QName.MIX_REFERENCEABLE);
+        if (referenceableOld && !referenceableNew) {
+            // node would become non-referenceable;
+            // make sure no references exist
+            PropertyIterator iter = getReferences();
+            if (iter.hasNext()) {
+                throw new ConstraintViolationException(
+                        "the new primary type cannot be set as it would render "
+                                + "this node 'non-referenceable' while it is still being "
+                                + "referenced through at least one property of type REFERENCE");
+            }
+        }
+
+        // do the actual modifications in content as mandated by the new primary type
+
+        // modify the state of this node
+        NodeState thisState = (NodeState) getOrCreateTransientItemState();
+        thisState.setNodeTypeName(ntName);
+
+        // set jcr:primaryType property
+        internalSetProperty(QName.JCR_PRIMARYTYPE, InternalValue.create(ntName));
+
+        // walk through properties and child nodes and remove those that
+        // are not included in the new node type
+        if (!removedDefs.isEmpty()) {
+            // use temp set to avoid ConcurrentModificationException
+            HashSet set = new HashSet(thisState.getPropertyNames());
+            for (Iterator iter = set.iterator(); iter.hasNext();) {
+                QName propName = (QName) iter.next();
+                try {
+                    PropertyState propState =
+                            (PropertyState) stateMgr.getItemState(
+                                    new PropertyId(thisState.getNodeId(), propName));
+                    if (removedDefs.contains(ntReg.getPropDef(propState.getDefinitionId()))) {
+                        removeChildProperty(propName);
+                    }
+                } catch (ItemStateException ise) {
+                    String msg = propName + ": failed to retrieve property state";
+                    log.error(msg, ise);
+                    throw new RepositoryException(msg, ise);
+                }
+            }
+            // use temp array to avoid ConcurrentModificationException
+            ArrayList list = new ArrayList(thisState.getChildNodeEntries());
+            // start from tail to avoid problems with same-name siblings
+            for (int i = list.size() - 1; i >= 0; i--) {
+                NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) list.get(i);
+                try {
+                    NodeState nodeState =
+                            (NodeState) stateMgr.getItemState(entry.getId());
+                    if (removedDefs.contains(ntReg.getNodeDef(nodeState.getDefinitionId()))) {
+                        removeChildNode(entry.getName(), entry.getIndex());
+                    }
+                } catch (ItemStateException ise) {
+                    String msg = entry.getName() + ": failed to retrieve node state";
+                    log.error(msg, ise);
+                    throw new RepositoryException(msg, ise);
+                }
+            }
+        }
+
+        // create new 'auto-create' items
+        for (Iterator iter = addedDefs.iterator(); iter.hasNext();) {
+            ItemDef def = (ItemDef) iter.next();
+            if (def.isAutoCreated()) {
+                if (def.definesNode()) {
+                    NodeDefinitionImpl nd = ntMgr.getNodeDefinition(((NodeDef) def).getId());
+                    createChildNode(nd.getQName(), nd, (NodeTypeImpl) nd.getDefaultPrimaryType(), null);
+                } else {
+                    PropertyDefinitionImpl pd = ntMgr.getPropertyDefinition(((PropDef) def).getId());
+                    createChildProperty(pd.getQName(), pd.getRequiredType(), pd);
+                }
+            }
         }
     }
 }