Search in sources :

Example 6 with EffectiveNodeType

use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.

the class NodeImpl method setPrimaryType.

/**
     * {@inheritDoc}
     */
public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
    // check state of this instance
    sanityCheck();
    // make sure this node is checked-out, neither protected nor locked and
    // the editing session has sufficient permission to change the primary type.
    int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
    sessionContext.getItemValidator().checkModify(this, options, Permission.NODE_TYPE_MNGMT);
    final NodeState state = data.getNodeState();
    if (state.getParentId() == null) {
        String msg = "changing the primary type of the root node is not supported";
        log.debug(msg);
        throw new RepositoryException(msg);
    }
    Name ntName = sessionContext.getQName(nodeTypeName);
    if (ntName.equals(state.getNodeTypeName())) {
        log.debug("Node already has " + nodeTypeName + " as primary node type.");
        return;
    }
    NodeTypeManagerImpl ntMgr = sessionContext.getNodeTypeManager();
    NodeType nt = ntMgr.getNodeType(ntName);
    if (nt.isMixin()) {
        throw new ConstraintViolationException(nodeTypeName + ": not a primary node type.");
    } else if (nt.isAbstract()) {
        throw new ConstraintViolationException(nodeTypeName + ": is an abstract 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, entAll;
    try {
        entNew = ntReg.getEffectiveNodeType(ntName);
        entOld = ntReg.getEffectiveNodeType(state.getNodeTypeName());
        // try to build new effective node type (will throw in case of conflicts)
        entAll = ntReg.getEffectiveNodeType(ntName, state.getMixinTypeNames());
    } catch (NodeTypeConflictException ntce) {
        throw new ConstraintViolationException(ntce.getMessage());
    }
    // get applicable definition for this node using new primary type
    QNodeDefinition nodeDef;
    try {
        NodeImpl parent = (NodeImpl) getParent();
        nodeDef = parent.getApplicableChildNodeDefinition(getQName(), ntName).unwrap();
    } catch (RepositoryException re) {
        String msg = this + ": no applicable definition found in parent node's node type";
        log.debug(msg);
        throw new ConstraintViolationException(msg, re);
    }
    if (!nodeDef.equals(itemMgr.getDefinition(state).unwrap())) {
        onRedefine(nodeDef);
    }
    Set<QItemDefinition> oldDefs = new HashSet<QItemDefinition>(Arrays.asList(entOld.getAllItemDefs()));
    Set<QItemDefinition> newDefs = new HashSet<QItemDefinition>(Arrays.asList(entNew.getAllItemDefs()));
    Set<QItemDefinition> allDefs = new HashSet<QItemDefinition>(Arrays.asList(entAll.getAllItemDefs()));
    // added child item definitions
    Set<QItemDefinition> addedDefs = new HashSet<QItemDefinition>(newDefs);
    addedDefs.removeAll(oldDefs);
    // referential integrity check
    boolean referenceableOld = entOld.includesNodeType(NameConstants.MIX_REFERENCEABLE);
    boolean referenceableNew = entNew.includesNodeType(NameConstants.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(NameConstants.JCR_PRIMARYTYPE, InternalValue.create(ntName));
    // walk through properties and child nodes and change definition as necessary
    // use temp set to avoid ConcurrentModificationException
    HashSet<Name> set = new HashSet<Name>(thisState.getPropertyNames());
    for (Name propName : set) {
        try {
            PropertyState propState = (PropertyState) stateMgr.getItemState(new PropertyId(thisState.getNodeId(), propName));
            if (!allDefs.contains(itemMgr.getDefinition(propState).unwrap())) {
                // redefine property if possible
                try {
                    PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
                    if (prop.getDefinition().isProtected()) {
                        // remove 'orphaned' protected properties immediately
                        removeChildProperty(propName);
                        continue;
                    }
                    PropertyDefinitionImpl pdi = getApplicablePropertyDefinition(propName, propState.getType(), propState.isMultiValued(), false);
                    if (pdi.getRequiredType() != PropertyType.UNDEFINED && pdi.getRequiredType() != propState.getType()) {
                        // value conversion required
                        if (propState.isMultiValued()) {
                            // convert value
                            Value[] values = ValueHelper.convert(prop.getValues(), pdi.getRequiredType(), getSession().getValueFactory());
                            // redefine property
                            prop.onRedefine(pdi.unwrap());
                            // set converted values
                            prop.setValue(values);
                        } else {
                            // convert value
                            Value value = ValueHelper.convert(prop.getValue(), pdi.getRequiredType(), getSession().getValueFactory());
                            // redefine property
                            prop.onRedefine(pdi.unwrap());
                            // set converted values
                            prop.setValue(value);
                        }
                    } else {
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                    }
                    // update collection of added definitions
                    addedDefs.remove(pdi.unwrap());
                } catch (ValueFormatException vfe) {
                    // value conversion failed, remove it
                    removeChildProperty(propName);
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this property,
                    // remove it
                    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<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(thisState.getChildNodeEntries());
    // start from tail to avoid problems with same-name siblings
    for (int i = list.size() - 1; i >= 0; i--) {
        ChildNodeEntry entry = list.get(i);
        try {
            NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
            if (!allDefs.contains(itemMgr.getDefinition(nodeState).unwrap())) {
                // redefine node if possible
                try {
                    NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
                    if (node.getDefinition().isProtected()) {
                        // remove 'orphaned' protected child node immediately
                        removeChildNode(entry.getId());
                        continue;
                    }
                    NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(entry.getName(), nodeState.getNodeTypeName());
                    // redefine node
                    node.onRedefine(ndi.unwrap());
                    // update collection of added definitions
                    addedDefs.remove(ndi.unwrap());
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this child node,
                    // remove it
                    removeChildNode(entry.getId());
                }
            }
        } catch (ItemStateException ise) {
            String msg = entry.getName() + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // type and at the same time were not present with the old nt
    for (QItemDefinition def : addedDefs) {
        if (def.isAutoCreated()) {
            if (def.definesNode()) {
                NodeDefinitionImpl ndi = ntMgr.getNodeDefinition((QNodeDefinition) def);
                createChildNode(def.getName(), (NodeTypeImpl) ndi.getDefaultPrimaryType(), null);
            } else {
                PropertyDefinitionImpl pdi = ntMgr.getPropertyDefinition((QPropertyDefinition) def);
                createChildProperty(pdi.unwrap().getName(), pdi.getRequiredType(), pdi);
            }
        }
    }
}
Also used : NodeState(org.apache.jackrabbit.core.state.NodeState) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) ArrayList(java.util.ArrayList) Name(org.apache.jackrabbit.spi.Name) NodeDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) HashSet(java.util.HashSet) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) PropertyIterator(javax.jcr.PropertyIterator) PropertyDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) RepositoryException(javax.jcr.RepositoryException) QNodeDefinition(org.apache.jackrabbit.spi.QNodeDefinition) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) PropertyState(org.apache.jackrabbit.core.state.PropertyState) PropertyId(org.apache.jackrabbit.core.id.PropertyId) InvalidItemStateException(javax.jcr.InvalidItemStateException) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) NodeType(javax.jcr.nodetype.NodeType) Value(javax.jcr.Value) InternalValue(org.apache.jackrabbit.core.value.InternalValue) ValueFormatException(javax.jcr.ValueFormatException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)

Example 7 with EffectiveNodeType

use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.

the class NodeStateEx method moveFrom.

/**
     * Moves the source node to this node using the given name.
     * @param src shareable source node
     * @param name name of new node
     * @param createShare if <code>true</code> a share is created instead.
     * @return child node
     * @throws RepositoryException if an error occurs
     */
public NodeStateEx moveFrom(NodeStateEx src, Name name, boolean createShare) throws RepositoryException {
    if (name == null) {
        name = src.getName();
    }
    EffectiveNodeType ent = getEffectiveNodeType();
    // (4) check for name collisions
    QNodeDefinition def;
    try {
        def = ent.getApplicableChildNodeDef(name, nodeState.getNodeTypeName(), ntReg);
    } catch (RepositoryException re) {
        String msg = "no definition found in parent node's node type for new node";
        throw new ConstraintViolationException(msg, re);
    }
    ChildNodeEntry cne = nodeState.getChildNodeEntry(name, 1);
    if (cne != null) {
        // check same-name sibling setting of new node
        if (!def.allowsSameNameSiblings()) {
            throw new ItemExistsException(getNodeId() + "/" + name);
        }
        NodeState existingChild;
        try {
            // check same-name sibling setting of existing node
            existingChild = (NodeState) stateMgr.getItemState(cne.getId());
        } catch (ItemStateException e) {
            throw new RepositoryException(e);
        }
        QNodeDefinition existingChildDef = ent.getApplicableChildNodeDef(cne.getName(), existingChild.getNodeTypeName(), ntReg);
        if (!existingChildDef.allowsSameNameSiblings()) {
            throw new ItemExistsException(existingChild.toString());
        }
    } else {
        // check if 'add' is allowed
        if (getDefinition().isProtected()) {
            String msg = "not allowed to modify a protected node";
            throw new ConstraintViolationException(msg);
        }
    }
    if (createShare) {
        // (5) do clone operation
        NodeId parentId = getNodeId();
        src.addShareParent(parentId);
        // attach to this parent
        nodeState.addChildNodeEntry(name, src.getNodeId());
        if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
            nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
        }
        return new NodeStateEx(stateMgr, ntReg, src.getState(), name);
    } else {
        // detach from parent
        NodeStateEx parent = getNode(src.getParentId());
        parent.nodeState.removeChildNodeEntry(src.getNodeId());
        if (parent.nodeState.getStatus() == ItemState.STATUS_EXISTING) {
            parent.nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
        }
        // attach to this parent
        nodeState.addChildNodeEntry(name, src.getNodeId());
        if (nodeState.getStatus() == ItemState.STATUS_EXISTING) {
            nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
        }
        NodeState srcState = src.getState();
        srcState.setParentId(getNodeId());
        if (srcState.getStatus() == ItemState.STATUS_EXISTING) {
            srcState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
        }
        return new NodeStateEx(stateMgr, ntReg, srcState, name);
    }
}
Also used : EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) NodeState(org.apache.jackrabbit.core.state.NodeState) ItemExistsException(javax.jcr.ItemExistsException) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) NodeId(org.apache.jackrabbit.core.id.NodeId) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) RepositoryException(javax.jcr.RepositoryException) QNodeDefinition(org.apache.jackrabbit.spi.QNodeDefinition) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException)

Example 8 with EffectiveNodeType

use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.

the class WorkspaceImporter method resolveUUIDConflict.

/**
     * @param parent parent node state
     * @param conflicting conflicting node state
     * @param nodeInfo the node info
     * @return the resolved node state
     * @throws RepositoryException if an error occurs
     */
protected NodeState resolveUUIDConflict(NodeState parent, NodeState conflicting, NodeInfo nodeInfo) throws RepositoryException {
    NodeState node;
    if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW) {
        // create new with new uuid:
        // check if new node can be added (check access rights &
        // node type constraints only, assume locking & versioning status
        // and retention/hold has already been checked on ancestor)
        itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS);
        node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), null);
        // remember uuid mapping
        EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
        if (ent.includesNodeType(NameConstants.MIX_REFERENCEABLE)) {
            refTracker.mappedId(nodeInfo.getId(), node.getNodeId());
        }
    } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW) {
        // new node and share with existing
        if (conflicting.isShareable()) {
            itemOps.clone(conflicting, parent, nodeInfo.getName());
            return null;
        }
        String msg = "a node with uuid " + nodeInfo.getId() + " already exists!";
        log.debug(msg);
        throw new ItemExistsException(msg);
    } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING) {
        // make sure conflicting node is not importTarget or an ancestor thereof
        Path p0 = hierMgr.getPath(importTarget.getNodeId());
        Path p1 = hierMgr.getPath(conflicting.getNodeId());
        try {
            if (p1.equals(p0) || p1.isAncestorOf(p0)) {
                String msg = "cannot remove ancestor node";
                log.debug(msg);
                throw new ConstraintViolationException(msg);
            }
        } catch (MalformedPathException mpe) {
            // should never get here...
            String msg = "internal error: failed to determine degree of relationship";
            log.error(msg, mpe);
            throw new RepositoryException(msg, mpe);
        }
        // remove conflicting:
        // check if conflicting can be removed
        // (access rights, node type constraints, locking & versioning status)
        itemOps.checkRemoveNode(conflicting, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
        // do remove conflicting (recursive)
        itemOps.removeNodeState(conflicting);
        // create new with given uuid:
        // check if new node can be added (check access rights &
        // node type constraints only, assume locking & versioning status
        // and retention/hold has already been checked on ancestor)
        itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_CONSTRAINTS);
        // do create new node
        node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), nodeInfo.getId());
    } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING) {
        NodeId parentId = conflicting.getParentId();
        if (parentId == null) {
            String msg = "root node cannot be replaced";
            log.debug(msg);
            throw new RepositoryException(msg);
        }
        // 'replace' current parent with parent of conflicting
        try {
            parent = itemOps.getNodeState(parentId);
        } catch (ItemNotFoundException infe) {
            // should never get here...
            String msg = "internal error: failed to retrieve parent state";
            log.error(msg, infe);
            throw new RepositoryException(msg, infe);
        }
        // remove conflicting:
        // check if conflicting can be removed
        // (access rights, node type constraints, locking & versioning status)
        itemOps.checkRemoveNode(conflicting, BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
        // 'replace' is actually a 'remove existing/add new' operation;
        // this unfortunately changes the order of the parent's
        // child node entries (JCR-1055);
        // => backup list of child node entries beforehand in order
        // to restore it afterwards
        ChildNodeEntry cneConflicting = parent.getChildNodeEntry(nodeInfo.getId());
        List<ChildNodeEntry> cneList = new ArrayList<ChildNodeEntry>(parent.getChildNodeEntries());
        // do remove conflicting (recursive)
        itemOps.removeNodeState(conflicting);
        // create new with given uuid at same location as conflicting:
        // check if new node can be added at other location
        // (access rights, node type constraints, locking & versioning
        // status and retention/hold)
        itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), BatchedItemOperations.CHECK_ACCESS | BatchedItemOperations.CHECK_LOCK | BatchedItemOperations.CHECK_CHECKED_OUT | BatchedItemOperations.CHECK_CONSTRAINTS | BatchedItemOperations.CHECK_HOLD | BatchedItemOperations.CHECK_RETENTION);
        // do create new node
        node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), nodeInfo.getId());
        // restore list of child node entries (JCR-1055)
        if (cneConflicting.getName().equals(nodeInfo.getName())) {
            // restore original child node list
            parent.setChildNodeEntries(cneList);
        } else {
            // replace child node entry with different name
            // but preserving original position
            parent.removeAllChildNodeEntries();
            for (ChildNodeEntry cne : cneList) {
                if (cne.getId().equals(nodeInfo.getId())) {
                    // replace entry with different name
                    parent.addChildNodeEntry(nodeInfo.getName(), nodeInfo.getId());
                } else {
                    parent.addChildNodeEntry(cne.getName(), cne.getId());
                }
            }
        }
    } else {
        String msg = "unknown uuidBehavior: " + uuidBehavior;
        log.debug(msg);
        throw new RepositoryException(msg);
    }
    return node;
}
Also used : Path(org.apache.jackrabbit.spi.Path) NodeState(org.apache.jackrabbit.core.state.NodeState) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) MalformedPathException(org.apache.jackrabbit.spi.commons.conversion.MalformedPathException) RepositoryException(javax.jcr.RepositoryException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) ItemExistsException(javax.jcr.ItemExistsException) NodeId(org.apache.jackrabbit.core.id.NodeId) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) ArrayList(java.util.ArrayList) List(java.util.List) ItemNotFoundException(javax.jcr.ItemNotFoundException)

Example 9 with EffectiveNodeType

use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.

the class WorkspaceImporter method postProcessNode.

/**
     * Post-process imported node (initialize properties with special
     * semantics etc.)
     *
     * @param node the node state
     * @throws RepositoryException if an error occurs
     */
protected void postProcessNode(NodeState node) throws RepositoryException {
    /**
         * special handling required for properties with special semantics
         * (e.g. those defined by mix:referenceable, mix:versionable,
         * mix:lockable, et.al.)
         *
         * todo FIXME delegate to 'node type instance handler'
         */
    EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
    if (ent.includesNodeType(NameConstants.MIX_SIMPLE_VERSIONABLE)) {
        /**
             * check if there's already a version history for that
             * node; this would e.g. be the case if a versionable node
             * had been exported, removed and re-imported with either
             * IMPORT_UUID_COLLISION_REMOVE_EXISTING or
             * IMPORT_UUID_COLLISION_REPLACE_EXISTING;
             * otherwise create a new version history
             */
        VersionHistoryInfo history = versionManager.getVersionHistory(session, node, null);
        InternalValue historyId = InternalValue.create(history.getVersionHistoryId());
        InternalValue versionId = InternalValue.create(history.getRootVersionId());
        // jcr:isCheckedOut
        conditionalAddProperty(node, NameConstants.JCR_ISCHECKEDOUT, PropertyType.BOOLEAN, false, InternalValue.create(true));
        // set extra properties only for full versionable nodes
        if (ent.includesNodeType(NameConstants.MIX_VERSIONABLE)) {
            // jcr:versionHistory
            conditionalAddProperty(node, NameConstants.JCR_VERSIONHISTORY, PropertyType.REFERENCE, false, historyId);
            // jcr:baseVersion
            conditionalAddProperty(node, NameConstants.JCR_BASEVERSION, PropertyType.REFERENCE, false, versionId);
            // jcr:predecessors
            conditionalAddProperty(node, NameConstants.JCR_PREDECESSORS, PropertyType.REFERENCE, true, versionId);
        }
    }
}
Also used : EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) VersionHistoryInfo(org.apache.jackrabbit.core.version.VersionHistoryInfo) InternalValue(org.apache.jackrabbit.core.value.InternalValue)

Example 10 with EffectiveNodeType

use of org.apache.jackrabbit.core.nodetype.EffectiveNodeType in project jackrabbit by apache.

the class NodeImpl method setMixins.

/**
     * {@inheritDoc}
     */
public void setMixins(String[] mixinNames) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
    // check state of this instance
    sanityCheck();
    NodeTypeManagerImpl ntMgr = sessionContext.getNodeTypeManager();
    Set<Name> newMixins = new HashSet<Name>();
    for (String name : mixinNames) {
        Name qName = sessionContext.getQName(name);
        if (!ntMgr.getNodeType(qName).isMixin()) {
            throw new RepositoryException(sessionContext.getJCRName(qName) + " is not a mixin node type");
        }
        newMixins.add(qName);
    }
    // make sure this node is checked-out, neither protected nor locked and
    // the editing session has sufficient permission to change the mixin types.
    // special handling of mix:(simple)versionable. since adding the
    // mixin alters the version storage jcr:versionManagement privilege
    // is required in addition.
    int permissions = Permission.NODE_TYPE_MNGMT;
    if (newMixins.contains(MIX_VERSIONABLE) || newMixins.contains(MIX_SIMPLE_VERSIONABLE)) {
        permissions |= Permission.VERSION_MNGMT;
    }
    int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD;
    sessionContext.getItemValidator().checkModify(this, options, permissions);
    final NodeState state = data.getNodeState();
    // build effective node type of primary type & new mixin's
    // in order to detect conflicts
    NodeTypeRegistry ntReg = ntMgr.getNodeTypeRegistry();
    EffectiveNodeType entNew, entOld, entAll;
    try {
        entNew = ntReg.getEffectiveNodeType(newMixins);
        entOld = ntReg.getEffectiveNodeType(state.getMixinTypeNames());
        // try to build new effective node type (will throw in case of conflicts)
        entAll = ntReg.getEffectiveNodeType(state.getNodeTypeName(), newMixins);
    } catch (NodeTypeConflictException ntce) {
        throw new ConstraintViolationException(ntce.getMessage());
    }
    // added child item definitions
    Set<QItemDefinition> addedDefs = new HashSet<QItemDefinition>(Arrays.asList(entNew.getAllItemDefs()));
    addedDefs.removeAll(Arrays.asList(entOld.getAllItemDefs()));
    // referential integrity check
    boolean referenceableOld = getEffectiveNodeType().includesNodeType(NameConstants.MIX_REFERENCEABLE);
    boolean referenceableNew = entAll.includesNodeType(NameConstants.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 mixin types 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");
        }
    }
    // gather currently assigned definitions *before* doing actual modifications
    Map<ItemId, ItemDefinition> oldDefs = new HashMap<ItemId, ItemDefinition>();
    for (Name name : getNodeState().getPropertyNames()) {
        PropertyId id = new PropertyId(getNodeId(), name);
        try {
            PropertyState propState = (PropertyState) stateMgr.getItemState(id);
            oldDefs.put(id, itemMgr.getDefinition(propState));
        } catch (ItemStateException ise) {
            String msg = name + ": failed to retrieve property state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    for (ChildNodeEntry cne : getNodeState().getChildNodeEntries()) {
        try {
            NodeState nodeState = (NodeState) stateMgr.getItemState(cne.getId());
            oldDefs.put(cne.getId(), itemMgr.getDefinition(nodeState));
        } catch (ItemStateException ise) {
            String msg = cne + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // now do the actual modifications in content as mandated by the new mixins
    // modify the state of this node
    NodeState thisState = (NodeState) getOrCreateTransientItemState();
    thisState.setMixinTypeNames(newMixins);
    // set jcr:mixinTypes property
    setMixinTypesProperty(newMixins);
    // walk through properties and child nodes and change definition as necessary
    // use temp set to avoid ConcurrentModificationException
    HashSet<Name> set = new HashSet<Name>(thisState.getPropertyNames());
    for (Name propName : set) {
        PropertyState propState = null;
        try {
            propState = (PropertyState) stateMgr.getItemState(new PropertyId(thisState.getNodeId(), propName));
            // the following call triggers ConstraintViolationException
            // if there isn't any suitable definition anymore
            itemMgr.getDefinition(propState);
        } catch (ConstraintViolationException cve) {
            // redefine property if possible
            try {
                if (oldDefs.get(propState.getId()).isProtected()) {
                    // remove 'orphaned' protected properties immediately
                    removeChildProperty(propName);
                    continue;
                }
                PropertyDefinitionImpl pdi = getApplicablePropertyDefinition(propName, propState.getType(), propState.isMultiValued(), false);
                PropertyImpl prop = (PropertyImpl) itemMgr.getItem(propState.getId());
                if (pdi.getRequiredType() != PropertyType.UNDEFINED && pdi.getRequiredType() != propState.getType()) {
                    // value conversion required
                    if (propState.isMultiValued()) {
                        // convert value
                        Value[] values = ValueHelper.convert(prop.getValues(), pdi.getRequiredType(), getSession().getValueFactory());
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                        // set converted values
                        prop.setValue(values);
                    } else {
                        // convert value
                        Value value = ValueHelper.convert(prop.getValue(), pdi.getRequiredType(), getSession().getValueFactory());
                        // redefine property
                        prop.onRedefine(pdi.unwrap());
                        // set converted values
                        prop.setValue(value);
                    }
                } else {
                    // redefine property
                    prop.onRedefine(pdi.unwrap());
                }
                // update collection of added definitions
                addedDefs.remove(pdi.unwrap());
            } catch (ValueFormatException vfe) {
                // value conversion failed, remove it
                removeChildProperty(propName);
            } catch (ConstraintViolationException cve1) {
                // no suitable definition found for this property,
                // remove it
                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<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(thisState.getChildNodeEntries());
    // start from tail to avoid problems with same-name siblings
    for (int i = list.size() - 1; i >= 0; i--) {
        ChildNodeEntry entry = list.get(i);
        NodeState nodeState = null;
        try {
            nodeState = (NodeState) stateMgr.getItemState(entry.getId());
            // the following call triggers ConstraintViolationException
            // if there isn't any suitable definition anymore
            itemMgr.getDefinition(nodeState);
        } catch (ConstraintViolationException cve) {
            // redefine node if possible
            try {
                if (oldDefs.get(nodeState.getId()).isProtected()) {
                    // remove 'orphaned' protected child node immediately
                    removeChildNode(entry.getId());
                    continue;
                }
                NodeDefinitionImpl ndi = getApplicableChildNodeDefinition(entry.getName(), nodeState.getNodeTypeName());
                NodeImpl node = (NodeImpl) itemMgr.getItem(nodeState.getId());
                // redefine node
                node.onRedefine(ndi.unwrap());
                // update collection of added definitions
                addedDefs.remove(ndi.unwrap());
            } catch (ConstraintViolationException cve1) {
                // no suitable definition found for this child node,
                // remove it
                removeChildNode(entry.getId());
            }
        } catch (ItemStateException ise) {
            String msg = entry + ": failed to retrieve node state";
            log.error(msg, ise);
            throw new RepositoryException(msg, ise);
        }
    }
    // and at the same time were not present with the old mixins
    for (QItemDefinition def : addedDefs) {
        if (def.isAutoCreated()) {
            if (def.definesNode()) {
                NodeDefinitionImpl ndi = ntMgr.getNodeDefinition((QNodeDefinition) def);
                createChildNode(def.getName(), (NodeTypeImpl) ndi.getDefaultPrimaryType(), null);
            } else {
                PropertyDefinitionImpl pdi = ntMgr.getPropertyDefinition((QPropertyDefinition) def);
                createChildProperty(pdi.unwrap().getName(), pdi.getRequiredType(), pdi);
            }
        }
    }
}
Also used : NodeState(org.apache.jackrabbit.core.state.NodeState) HashMap(java.util.HashMap) NodeTypeConflictException(org.apache.jackrabbit.core.nodetype.NodeTypeConflictException) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) ItemDefinition(javax.jcr.nodetype.ItemDefinition) ArrayList(java.util.ArrayList) ItemId(org.apache.jackrabbit.core.id.ItemId) Name(org.apache.jackrabbit.spi.Name) NodeDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl) ConstraintViolationException(javax.jcr.nodetype.ConstraintViolationException) NodeTypeManagerImpl(org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl) HashSet(java.util.HashSet) ChildNodeEntry(org.apache.jackrabbit.core.state.ChildNodeEntry) PropertyIterator(javax.jcr.PropertyIterator) PropertyDefinitionImpl(org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) RepositoryException(javax.jcr.RepositoryException) QItemDefinition(org.apache.jackrabbit.spi.QItemDefinition) PropertyId(org.apache.jackrabbit.core.id.PropertyId) PropertyState(org.apache.jackrabbit.core.state.PropertyState) InvalidItemStateException(javax.jcr.InvalidItemStateException) ItemStateException(org.apache.jackrabbit.core.state.ItemStateException) EffectiveNodeType(org.apache.jackrabbit.core.nodetype.EffectiveNodeType) Value(javax.jcr.Value) InternalValue(org.apache.jackrabbit.core.value.InternalValue) ValueFormatException(javax.jcr.ValueFormatException) NodeTypeRegistry(org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)

Aggregations

EffectiveNodeType (org.apache.jackrabbit.core.nodetype.EffectiveNodeType)24 NodeState (org.apache.jackrabbit.core.state.NodeState)16 RepositoryException (javax.jcr.RepositoryException)15 Name (org.apache.jackrabbit.spi.Name)14 ConstraintViolationException (javax.jcr.nodetype.ConstraintViolationException)13 NodeTypeConflictException (org.apache.jackrabbit.core.nodetype.NodeTypeConflictException)10 NodeTypeRegistry (org.apache.jackrabbit.core.nodetype.NodeTypeRegistry)10 ChildNodeEntry (org.apache.jackrabbit.core.state.ChildNodeEntry)10 NodeId (org.apache.jackrabbit.core.id.NodeId)9 ItemStateException (org.apache.jackrabbit.core.state.ItemStateException)9 QNodeDefinition (org.apache.jackrabbit.spi.QNodeDefinition)7 ItemExistsException (javax.jcr.ItemExistsException)6 PropertyId (org.apache.jackrabbit.core.id.PropertyId)6 NodeTypeManagerImpl (org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl)6 InternalValue (org.apache.jackrabbit.core.value.InternalValue)6 QItemDefinition (org.apache.jackrabbit.spi.QItemDefinition)6 HashSet (java.util.HashSet)5 ItemNotFoundException (javax.jcr.ItemNotFoundException)5 PropertyState (org.apache.jackrabbit.core.state.PropertyState)5 Path (org.apache.jackrabbit.spi.Path)5