Package org.apache.jackrabbit.jcr2spi.state

Source Code of org.apache.jackrabbit.jcr2spi.state.ChangeLog

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.jcr2spi.state;

import org.apache.jackrabbit.jcr2spi.operation.Operation;
import org.apache.jackrabbit.jcr2spi.operation.AddNode;
import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
import org.apache.jackrabbit.jcr2spi.operation.SetMixin;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.commons.collections.iterators.IteratorChain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.RepositoryException;
import java.util.Iterator;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.HashSet;
import java.util.Collection;

/**
* Registers changes made to states and references and consolidates
* empty changes.
*/
public class ChangeLog {

    /**
     * Logger instance for this class.
     */
    private static final Logger log = LoggerFactory.getLogger(ChangeLog.class);

    /**
     * The changelog target: Root item of the tree whose changes are contained
     * in this changelog.
     */
    private final ItemState target;
    /**
     * Added states
     */
    private final Set addedStates = new LinkedHashSet();

    /**
     * Modified states
     */
    private final Set modifiedStates = new LinkedHashSet();

    /**
     * Deleted states
     */
    private final Set deletedStates = new LinkedHashSet();

    /**
     * Set of operations
     */
    private Set operations = new LinkedHashSet();

    /**
     *
     * @param target
     */
    ChangeLog(ItemState target) {
        this.target = target;
    }

    //-----------------------------------------------< Inform the ChangeLog >---
    /**
     * Add the given operation to the list of operations to be recorded within
     * the current update cycle of this ChangeLog.
     *
     * @param operation
     */
    public void addOperation(Operation operation) {
        operations.add(operation);
    }

    /**
     * A state has been added
     *
     * @param state state that has been added
     */
    public void added(ItemState state) {
        addedStates.add(state);
    }

    /**
     * A state has been modified. If the state is not a new state
     * (not in the collection of added ones), then add
     * it to the modified states collection.
     *
     * @param state state that has been modified
     */
    public void modified(ItemState state) {
        if (!addedStates.contains(state)) {
            modifiedStates.add(state);
        }
    }

    /**
     * A state has been deleted. If the state is not a new state
     * (not in the collection of added ones), then remove
     * it from the modified states collection and add it to the
     * deleted states collection.
     *
     * @param state state that has been deleted
     */
    public void deleted(ItemState state) {
        if (!addedStates.remove(state)) {
            modifiedStates.remove(state);
            deletedStates.add(state);
        }
    }

    /**
     * Call this method when this change log has been sucessfully persisted.
     * This implementation will call {@link ItemState#persisted(ChangeLog)
     * ItemState.refresh(this)} on the target item of this change log.
     */
    public void persisted() {
        target.persisted(this);
    }

    /**
     * Revert the changes listed within this changelog
     */
    public void undo() throws RepositoryException {
        // TODO: check if states are reverted in the correct order
        Iterator[] its = new Iterator[] {addedStates(), deletedStates(), modifiedStates()};
        IteratorChain chain = new IteratorChain(its);
        while (chain.hasNext()) {
            ItemState state = (ItemState) chain.next();
            state.getHierarchyEntry().revert();
        }
    }
    //----------------------< Retrieve information present in the ChangeLog >---
    /**
     *
     * @return
     */
    public ItemState getTarget() {
        return target;
    }

    /**
     *
     * @return
     */
    public boolean isEmpty() {
        return operations.isEmpty();
    }

    /**
     *
     * @return
     */
    public Iterator getOperations() {
        return operations.iterator();
    }

    /**
     * Return an iterator over all added states.
     *
     * @return iterator over all added states.
     */
    public Iterator addedStates() {
        return addedStates.iterator();
    }

    /**
     * Return an iterator over all modified states.
     *
     * @return iterator over all modified states.
     */
    public Iterator modifiedStates() {
        return modifiedStates.iterator();
    }

    /**
     * Return an iterator over all deleted states.
     *
     * @return iterator over all deleted states.
     */
    public Iterator deletedStates() {
        return deletedStates.iterator();
    }

    /**
     * Returns true, if this change log contains the given <code>ItemState</code>
     * in the set of transiently removed states.
     *
     * @param state
     * @return
     */
    public boolean containsDeletedState(ItemState state) {
        return deletedStates.contains(state);
    }

    /**
     * Removes the subset of this changelog represented by the given
     * <code>ChangeLog</code> from this changelog.
     *
     * @param subChangeLog remove all entries (states, operations) present in
     * the given changelog from this changelog.
     */
    public void removeAll(ChangeLog subChangeLog) {
        addedStates.removeAll(subChangeLog.addedStates);
        modifiedStates.removeAll(subChangeLog.modifiedStates);
        deletedStates.removeAll(subChangeLog.deletedStates);

        operations.removeAll(subChangeLog.operations);
    }

    /**
     * Adjust this ChangeLog according to the status change with the given
     * ItemState:
     * Remove all entries and operation related to the given ItemState, that
     * are not used any more (respecting the status change).
     *
     * @param state
     */
    public void statusChanged(ItemState state, int previousStatus) {
        switch (state.getStatus()) {
            case (Status.EXISTING):
                switch (previousStatus) {
                    case Status.EXISTING_MODIFIED:
                        // was modified and is now refreshed
                        modifiedStates.remove(state);
                        break;
                    case Status.EXISTING_REMOVED:
                        // was removed and is now refreshed
                        deletedStates.remove(state);
                        break;
                    case Status.STALE_MODIFIED:
                        // was modified and state and is now refreshed
                        modifiedStates.remove(state);
                        break;
                    case Status.NEW:
                        // was new and has been saved now
                        addedStates.remove(state);
                        break;
                }
                // TODO: check if correct: changelog gets cleared any way -> no need to remove operations
                break;
            case Status.EXISTING_MODIFIED:
                modified(state);
                break;
            case (Status.EXISTING_REMOVED):
                deleted(state);
                // removeAffectedOperations(state);
                break;
            case (Status.REMOVED):
                switch (previousStatus) {
                    case Status.EXISTING_REMOVED:
                        // was removed and is now saved
                        deletedStates.remove(state);
                        removeAffectedOperations(state);
                        break;
                    case Status.NEW:
                        newStateRemoved(state);
                        break;
                }
                break;
        }
    }

    private void newStateRemoved(ItemState state) {
        NodeEntry parentEntry = state.getHierarchyEntry().getParent();
        if (!parentEntry.isAvailable() || Status.isTerminal(parentEntry.getStatus())) {
            return; // TODO: check if correct
        }
        // was new and now removed again
        addedStates.remove(state);

        // remove any operations performed on the removed state
        removeAffectedOperations(state);

        /* remove the add-operation as well:
           since the affected state of an 'ADD' operation is the parent instead
           of the added-state, the set of operations need to be searched for the
           parent state && the proper operation type.
           SET_MIXIN is considered as a special case of adding a property
         */
        NodeState parent;
        try {
            parent = parentEntry.getNodeState();
        } catch (RepositoryException e) {
            // should never occur
            log.error("Internal error:", e);
            return;
        }
        for (Iterator it = operations.iterator(); it.hasNext();) {
            Operation op = (Operation) it.next();
            if (op instanceof AddNode) {
                AddNode operation = (AddNode) op;
                if (operation.getParentState() == parent
                        && operation.getNodeName().equals(state.getName())) {
                    // TODO: this will not work for name name siblings!
                    it.remove();
                    break;
                }
            } else if (op instanceof AddProperty) {
                AddProperty operation = (AddProperty) op;
                if (operation.getParentState() == parent
                        && operation.getPropertyName().equals(state.getName())) {
                    it.remove();
                    break;
                }
            } else if (op instanceof SetMixin &&
                    NameConstants.JCR_MIXINTYPES.equals(state.getName()) &&
                    ((SetMixin)op).getNodeState() == parent) {
                it.remove();
                break;
            }
        }
    }

    private void removeAffectedOperations(ItemState state) {
        for (Iterator it = operations.iterator(); it.hasNext();) {
            Operation op = (Operation) it.next();
            if (op.getAffectedItemStates().contains(state)) {
                it.remove();
            }
        }
    }

    /**
     * Make sure that this ChangeLog is totally 'self-contained'
     * and independant; items within the scope of this update operation
     * must not have 'external' dependencies;
     * (e.g. moving a node requires that the target node including both
     * old and new parents are saved)
     */
    public void checkIsSelfContained() throws ConstraintViolationException {
        Set affectedStates = new HashSet();
        affectedStates.addAll(modifiedStates);
        affectedStates.addAll(deletedStates);
        affectedStates.addAll(addedStates);

        // check if the affected states listed by the operations are all
        // listed in the modified,deleted or added states collected by this
        // changelog.
        Iterator it = getOperations();
        while (it.hasNext()) {
            Operation op = (Operation) it.next();
            Collection opStates = op.getAffectedItemStates();
            if (!affectedStates.containsAll(opStates)) {
                // need to save the parent as well
                String msg = "ChangeLog is not self contained.";
                throw new ConstraintViolationException(msg);
            }
        }
    }

    /**
     * Populates this <code>ChangeLog</code> with operations that are within the
     * scope of this change set.
     *
     * @param operations an Iterator of <code>Operation</code>s which are the
     *                   candidates to be included in this <code>ChangeLog</code>.
     */
    public void collectOperations(Iterator operations) {
        Set affectedStates = new HashSet();
        affectedStates.addAll(addedStates);
        affectedStates.addAll(deletedStates);
        affectedStates.addAll(modifiedStates);
        while (operations.hasNext()) {
            Operation op = (Operation) operations.next();
            Iterator states = op.getAffectedItemStates().iterator();
            while (states.hasNext()) {
                ItemState state = (ItemState) states.next();
                if (affectedStates.contains(state)) {
                    addOperation(op);
                    break;
                }
            }
        }
    }

    /**
     * Reset this change log, removing all members inside the
     * maps we built.
     */
    public void reset() {
        addedStates.clear();
        modifiedStates.clear();
        deletedStates.clear();
        // also clear all operations
        operations.clear();
    }

    //-------------------------------------------------------------< Object >---
    /**
     * Returns a string representation of this change log for diagnostic
     * purposes.
     *
     * @return a string representation of this change log
     */
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("{");
        buf.append("#addedStates=").append(addedStates.size());
        buf.append(", #modifiedStates=").append(modifiedStates.size());
        buf.append(", #deletedStates=").append(deletedStates.size());
        buf.append("}");
        return buf.toString();
    }
}
TOP

Related Classes of org.apache.jackrabbit.jcr2spi.state.ChangeLog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.