public Object perform()

in jackrabbit-core/src/main/java/org/apache/jackrabbit/core/RemoveMixinOperation.java [74:289]


    public Object perform(SessionContext context) throws RepositoryException {
        SessionImpl session = context.getSessionImpl();
        ItemManager itemMgr = context.getItemManager();
        SessionItemStateManager stateMgr = context.getItemStateManager();

        context.getItemValidator().checkModify(
                node,
                CHECK_LOCK | CHECK_CHECKED_OUT | CHECK_CONSTRAINTS | CHECK_HOLD,
                Permission.NODE_TYPE_MNGMT);

        // check if mixin is assigned
        NodeState state = node.getNodeState();
        if (!state.getMixinTypeNames().contains(mixinName)) {
            throw new NoSuchNodeTypeException(
                    "Mixin " + context.getJCRName(mixinName)
                    + " not included in " + node);
        }

        NodeTypeManagerImpl ntMgr = context.getNodeTypeManager();
        NodeTypeRegistry ntReg = context.getNodeTypeRegistry();

        // build effective node type of remaining mixin's & primary type
        Set<Name> remainingMixins = new HashSet<Name>(state.getMixinTypeNames());
        // remove name of target mixin
        remainingMixins.remove(mixinName);
        EffectiveNodeType entResulting;
        try {
            // build effective node type representing primary type
            // including remaining mixin's
            entResulting = ntReg.getEffectiveNodeType(
                    state.getNodeTypeName(), remainingMixins);
        } catch (NodeTypeConflictException e) {
            throw new ConstraintViolationException(e.getMessage(), e);
        }

        // mix:referenceable needs special handling because it has
        // special semantics:
        // it can only be removed if there no more references to this node
        NodeTypeImpl mixin = ntMgr.getNodeType(mixinName);
        if (isReferenceable(mixin)
                && !entResulting.includesNodeType(MIX_REFERENCEABLE)) {
            if (node.getReferences().hasNext()) {
                throw new ConstraintViolationException(
                        mixinName + " can not be removed:"
                        + " the node is being referenced through at least"
                        + " one property of type REFERENCE");
            }
        }

        // mix:lockable: the mixin cannot be removed if the node is
        // currently locked even if the editing session is the lock holder.
        if ((NameConstants.MIX_LOCKABLE.equals(mixinName)
                || mixin.isDerivedFrom(NameConstants.MIX_LOCKABLE))
                && !entResulting.includesNodeType(NameConstants.MIX_LOCKABLE)
                && node.isLocked()) {
            throw new ConstraintViolationException(
                    mixinName + " can not be removed: the node is locked.");
        }

        NodeState thisState = (NodeState) node.getOrCreateTransientItemState();

        // collect information about properties and nodes which require further
        // action as a result of the mixin removal; we need to do this *before*
        // actually changing the assigned mixin types, otherwise we wouldn't
        // be able to retrieve the current definition of an item.
        Map<PropertyId, PropertyDefinition> affectedProps =
            new HashMap<PropertyId, PropertyDefinition>();
        Map<ChildNodeEntry, NodeDefinition> affectedNodes =
            new HashMap<ChildNodeEntry, NodeDefinition>();
        try {
            Set<Name> names = thisState.getPropertyNames();
            for (Name propName : names) {
                PropertyId propId =
                    new PropertyId(thisState.getNodeId(), propName);
                PropertyState propState =
                    (PropertyState) stateMgr.getItemState(propId);
                PropertyDefinition oldDef = itemMgr.getDefinition(propState);
                // check if property has been defined by mixin type
                // (or one of its supertypes)
                NodeTypeImpl declaringNT =
                    (NodeTypeImpl) oldDef.getDeclaringNodeType();
                if (!entResulting.includesNodeType(declaringNT.getQName())) {
                    // the resulting effective node type doesn't include the
                    // node type that declared this property
                    affectedProps.put(propId, oldDef);
                }
            }

            List<ChildNodeEntry> entries = thisState.getChildNodeEntries();
            for (ChildNodeEntry entry : entries) {
                NodeState nodeState =
                    (NodeState) stateMgr.getItemState(entry.getId());
                NodeDefinition oldDef = itemMgr.getDefinition(nodeState);
                // check if node has been defined by mixin type
                // (or one of its supertypes)
                NodeTypeImpl declaringNT =
                    (NodeTypeImpl) oldDef.getDeclaringNodeType();
                if (!entResulting.includesNodeType(declaringNT.getQName())) {
                    // the resulting effective node type doesn't include the
                    // node type that declared this child node
                    affectedNodes.put(entry, oldDef);
                }
            }
        } catch (ItemStateException e) {
            throw new RepositoryException(
                    "Failed to determine effect of removing mixin "
                    + context.getJCRName(mixinName), e);
        }

        // modify the state of this node
        thisState.setMixinTypeNames(remainingMixins);
        // set jcr:mixinTypes property
        node.setMixinTypesProperty(remainingMixins);

        // process affected nodes & properties:
        // 1. try to redefine item based on the resulting
        //    new effective node type (see JCR-2130)
        // 2. remove item if 1. fails
        boolean success = false;
        try {
        	for (Map.Entry<PropertyId, PropertyDefinition> entry : affectedProps.entrySet()) {
        		PropertyId id = entry.getKey();
                PropertyImpl prop = (PropertyImpl) itemMgr.getItem(id);
                PropertyDefinition oldDef = entry.getValue();

                if (oldDef.isProtected()) {
                    // remove 'orphaned' protected properties immediately
                    node.removeChildProperty(id.getName());
                    continue;
                }
                // try to find new applicable definition first and
                // redefine property if possible (JCR-2130)
                try {
                    PropertyDefinitionImpl newDef =
                        node.getApplicablePropertyDefinition(
                            id.getName(), prop.getType(),
                            oldDef.isMultiple(), false);
                    if (newDef.getRequiredType() != PropertyType.UNDEFINED
                            && newDef.getRequiredType() != prop.getType()) {
                        // value conversion required
                        if (oldDef.isMultiple()) {
                            // convert value
                            Value[] values =
                                ValueHelper.convert(
                                        prop.getValues(),
                                        newDef.getRequiredType(),
                                        session.getValueFactory());
                            // redefine property
                            prop.onRedefine(newDef.unwrap());
                            // set converted values
                            prop.setValue(values);
                        } else {
                            // convert value
                            Value value =
                                ValueHelper.convert(
                                        prop.getValue(),
                                        newDef.getRequiredType(),
                                        session.getValueFactory());
                            // redefine property
                            prop.onRedefine(newDef.unwrap());
                            // set converted values
                            prop.setValue(value);
                        }
                    } else {
                        // redefine property
                        prop.onRedefine(newDef.unwrap());
                    }
                } catch (ValueFormatException vfe) {
                    // value conversion failed, remove it
                    node.removeChildProperty(id.getName());
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this property,
                    // remove it
                    node.removeChildProperty(id.getName());
                }
            }

            for (ChildNodeEntry entry : affectedNodes.keySet()) {
                NodeState nodeState = (NodeState) stateMgr.getItemState(entry.getId());
                NodeImpl childNode = (NodeImpl) itemMgr.getItem(entry.getId());
                NodeDefinition oldDef = affectedNodes.get(entry);

                if (oldDef.isProtected()) {
                    // remove 'orphaned' protected child node immediately
                    node.removeChildNode(entry.getId());
                    continue;
                }

                // try to find new applicable definition first and
                // redefine node if possible (JCR-2130)
                try {
                    NodeDefinitionImpl newDef =
                        node.getApplicableChildNodeDefinition(
                                entry.getName(),
                                nodeState.getNodeTypeName());
                    // redefine node
                    childNode.onRedefine(newDef.unwrap());
                } catch (ConstraintViolationException cve) {
                    // no suitable definition found for this child node,
                    // remove it
                    node.removeChildNode(entry.getId());
                }
            }
            success = true;
        } catch (ItemStateException e) {
            throw new RepositoryException(
                    "Failed to clean up child items defined by removed mixin "
                    + context.getJCRName(mixinName), e);
        } finally {
            if (!success) {
                // TODO JCR-1914: revert any changes made so far
            }
        }

        return this;
    }