public void createEventStates()

in jackrabbit-core/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java [147:444]


    public void createEventStates(NodeId rootNodeId, ChangeLog changes, ItemStateManager stateMgr) throws ItemStateException {
        // create a hierarchy manager, that is based on the ChangeLog and
        // the ItemStateProvider
        ChangeLogBasedHierarchyMgr hmgr =
            new ChangeLogBasedHierarchyMgr(rootNodeId, stateMgr, changes);

        /**
         * Important:
         * Do NOT change the sequence of events generated unless there's
         * a very good reason for it! Some internal SynchronousEventListener
         * implementations depend on the order of the events fired.
         * LockManagerImpl for example expects that for any given path a
         * childNodeRemoved event is fired before a childNodeAdded event.
         */

        // 1. modified items
        for (ItemState state : changes.modifiedStates()) {
            if (state.isNode()) {
                // node changed
                // covers the following cases:
                // 1) property added
                // 2) property removed
                // 3) child node added
                // 4) child node removed
                // 5) node moved/reordered
                // 6) node reordered
                // 7) shareable node added
                // 8) shareable node removed
                // cases 1) and 2) are detected with added and deleted states
                // on the PropertyState itself.
                // cases 3) and 4) are detected with added and deleted states
                // on the NodeState itself.
                // in case 5) two or three nodes change. two nodes are changed
                // when a child node is renamed. three nodes are changed when
                // a node is really moved. In any case we are only interested in
                // the node that actually got moved.
                // in case 6) only one node state changes. the state of the
                // parent node.
                // in case 7) parent of added shareable node has new child node
                // entry.
                // in case 8) parent of removed shareable node has removed child
                // node entry.
                NodeState n = (NodeState) state;

                if (n.hasOverlayedState()) {
                    NodeId oldParentId = n.getOverlayedState().getParentId();
                    NodeId newParentId = n.getParentId();
                    if (newParentId != null && !oldParentId.equals(newParentId) &&
                            !n.isShareable()) {
                        Path oldPath = getZombiePath(n.getNodeId(), hmgr);

                        // node moved
                        // generate node removed & node added event
                        NodeState oldParent;
                        try {
                            oldParent = (NodeState) changes.get(oldParentId);
                        } catch (NoSuchItemStateException e) {
                            // old parent has been deleted, retrieve from
                            // shared item state manager
                            oldParent = (NodeState) stateMgr.getItemState(oldParentId);
                        }
                        if (oldParent != null) {
                            NodeTypeImpl oldParentNodeType = getNodeType(oldParent, session);
                            events.add(EventState.childNodeRemoved(oldParentId,
                                    getParent(oldPath), n.getNodeId(),
                                    oldPath.getLastElement(),
                                    oldParentNodeType.getQName(),
                                    oldParent.getMixinTypeNames(), session));
                        } else {
                            // JCR-2298: In some cases the old parent node
                            // state is no longer available anywhere. Log an
                            // error since in this case we can't generate the
                            // correct REMOVE event.
                            log.error(
                                    "The old parent (node id " + oldParentId
                                    + ") of a moved node (old path "
                                    + oldPath + ") is no longer available."
                                    + " No REMOVE event generated!");
                        }

                        NodeState newParent = (NodeState) changes.get(newParentId);
                        NodeTypeImpl newParentNodeType = getNodeType(newParent, session);
                        Set<Name> mixins = newParent.getMixinTypeNames();
                        Path newPath = getPath(n.getNodeId(), hmgr);
                        events.add(EventState.childNodeAdded(newParentId,
                                getParent(newPath), n.getNodeId(),
                                newPath.getLastElement(),
                                newParentNodeType.getQName(),
                                mixins, session));

                        events.add(EventState.nodeMovedWithInfo(
                                newParentId, newPath, n.getNodeId(), oldPath,
                                newParentNodeType.getQName(), mixins,
                                session, false));
                    } else {
                        // a moved node always has a modified parent node
                        NodeState parent = null;
                        try {
                            // root node does not have a parent UUID
                            if (state.getParentId() != null) {
                                parent = (NodeState) changes.get(state.getParentId());
                            }
                        } catch (NoSuchItemStateException e) {
                            // should never happen actually. this would mean
                            // the parent of this modified node is deleted
                            String msg = "Parent of node " + state.getId() + " is deleted.";
                            log.error(msg);
                            throw new ItemStateException(msg, e);
                        }
                        if (parent != null) {
                            // check if node has been renamed
                            ChildNodeEntry moved = null;
                            for (ChildNodeEntry child : parent.getRemovedChildNodeEntries()) {
                                if (child.getId().equals(n.getNodeId())) {
                                    // found node re-added with different name
                                    moved = child;
                                }
                            }
                            if (moved != null) {
                                NodeTypeImpl nodeType = getNodeType(parent, session);
                                Set<Name> mixins = parent.getMixinTypeNames();
                                Path newPath = getPath(state.getId(), hmgr);
                                Path parentPath = getParent(newPath);
                                Path oldPath;
                                try {
                                    if (moved.getIndex() == 0) {
                                        oldPath = PathFactoryImpl.getInstance().create(parentPath, moved.getName(), false);
                                    } else {
                                        oldPath = PathFactoryImpl.getInstance().create(
                                                parentPath, moved.getName(), moved.getIndex(), false);
                                    }
                                } catch (RepositoryException e) {
                                    // should never happen actually
                                    String msg = "Malformed path for item: " + state.getId();
                                    log.error(msg);
                                    throw new ItemStateException(msg, e);
                                }
                                events.add(EventState.childNodeRemoved(
                                        parent.getNodeId(), parentPath,
                                        n.getNodeId(), oldPath.getLastElement(),
                                        nodeType.getQName(), mixins, session));

                                events.add(EventState.childNodeAdded(
                                        parent.getNodeId(), parentPath,
                                        n.getNodeId(), newPath.getLastElement(),
                                        nodeType.getQName(), mixins, session));

                                events.add(EventState.nodeMovedWithInfo(
                                        parent.getNodeId(), newPath, n.getNodeId(),
                                        oldPath, nodeType.getQName(), mixins,
                                        session, false));
                            }
                        }
                    }
                }

                // check if child nodes of modified node state have been reordered
                List<ChildNodeEntry> reordered = n.getReorderedChildNodeEntries();
                NodeTypeImpl nodeType = getNodeType(n, session);
                Set<Name> mixins = n.getMixinTypeNames();
                if (reordered.size() > 0) {
                    // create a node removed and a node added event for every
                    // reorder
                    for (ChildNodeEntry child : reordered) {
                        Path addedElem = getPathElement(child);
                        Path parentPath = getPath(n.getNodeId(), hmgr);
                        // get removed index
                        NodeState overlayed = (NodeState) n.getOverlayedState();
                        ChildNodeEntry entry = overlayed.getChildNodeEntry(child.getId());
                        if (entry == null) {
                            throw new ItemStateException("Unable to retrieve old child index for item: " + child.getId());
                        }
                        Path removedElem = getPathElement(entry);

                        events.add(EventState.childNodeRemoved(n.getNodeId(),
                                parentPath, child.getId(), removedElem,
                                nodeType.getQName(), mixins, session));

                        events.add(EventState.childNodeAdded(n.getNodeId(),
                                parentPath, child.getId(), addedElem,
                                nodeType.getQName(), mixins, session));

                        List<ChildNodeEntry> cne = n.getChildNodeEntries();
                        // index of the child node entry before which this
                        // child node entry was reordered
                        int idx = cne.indexOf(child) + 1;
                        Path beforeElem = null;
                        if (idx < cne.size()) {
                            beforeElem = getPathElement(cne.get(idx));
                        }

                        events.add(EventState.nodeReordered(n.getNodeId(),
                                parentPath, child.getId(), addedElem,
                                removedElem, beforeElem, nodeType.getQName(), mixins,
                                session, false));
                    }
                }

                // create events if n is shareable
                createShareableNodeEvents(n, changes, hmgr, stateMgr);
            } else {
                // property changed
                Path path = getPath(state.getId(), hmgr);
                NodeState parent = (NodeState) stateMgr.getItemState(state.getParentId());
                NodeTypeImpl nodeType = getNodeType(parent, session);
                Set<Name> mixins = parent.getMixinTypeNames();
                events.add(EventState.propertyChanged(state.getParentId(),
                        getParent(path), path.getLastElement(),
                        nodeType.getQName(), mixins, session));
            }
        }

        // 2. removed items
        for (ItemState state : changes.deletedStates()) {
            if (state.isNode()) {
                // node deleted
                NodeState n = (NodeState) state;
                NodeState parent = (NodeState) stateMgr.getItemState(n.getParentId());
                NodeTypeImpl nodeType = getNodeType(parent, session);
                Set<Name> mixins = parent.getMixinTypeNames();
                Path path = getZombiePath(state.getId(), hmgr);
                events.add(EventState.childNodeRemoved(n.getParentId(),
                        getParent(path),
                        n.getNodeId(),
                        path.getLastElement(),
                        nodeType.getQName(),
                        mixins,
                        session));

                // create events if n is shareable
                createShareableNodeEvents(n, changes, hmgr, stateMgr);
            } else {
                // property removed
                // only create an event if node still exists
                try {
                    NodeState n = (NodeState) changes.get(state.getParentId());
                    // node state exists -> only property removed
                    NodeTypeImpl nodeType = getNodeType(n, session);
                    Set<Name> mixins = n.getMixinTypeNames();
                    Path path = getZombiePath(state.getId(), hmgr);
                    events.add(EventState.propertyRemoved(state.getParentId(),
                            getParent(path),
                            path.getLastElement(),
                            nodeType.getQName(),
                            mixins,
                            session));
                } catch (NoSuchItemStateException e) {
                    // node removed as well -> do not create an event
                }
            }
        }

        // 3. added items
        for (ItemState state : changes.addedStates()) {
            if (state.isNode()) {
                // node created
                NodeState n = (NodeState) state;
                NodeId parentId = n.getParentId();
                // the parent of an added item is always modified or new
                NodeState parent = (NodeState) changes.get(parentId);
                if (parent == null) {
                    String msg = "Parent " + parentId + " must be changed as well.";
                    log.error(msg);
                    throw new ItemStateException(msg);
                }
                NodeTypeImpl nodeType = getNodeType(parent, session);
                Set<Name> mixins = parent.getMixinTypeNames();
                Path path = getPath(n.getNodeId(), hmgr);
                events.add(EventState.childNodeAdded(parentId,
                        getParent(path),
                        n.getNodeId(),
                        path.getLastElement(),
                        nodeType.getQName(),
                        mixins,
                        session));

                // create events if n is shareable
                createShareableNodeEvents(n, changes, hmgr, stateMgr);
            } else {
                // property created / set
                NodeState n = (NodeState) changes.get(state.getParentId());
                if (n == null) {
                    String msg = "Node " + state.getParentId() + " must be changed as well.";
                    log.error(msg);
                    throw new ItemStateException(msg);
                }
                NodeTypeImpl nodeType = getNodeType(n, session);
                Set<Name> mixins = n.getMixinTypeNames();
                Path path = getPath(state.getId(), hmgr);
                events.add(EventState.propertyAdded(state.getParentId(),
                        getParent(path),
                        path.getLastElement(),
                        nodeType.getQName(),
                        mixins,
                        session));
            }
        }
    }