Package org.apache.jackrabbit.oak.core

Source Code of org.apache.jackrabbit.oak.core.TreeImpl

/*
* 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.oak.core;

import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.util.Function1;
import org.apache.jackrabbit.oak.util.Iterators;

import java.util.Iterator;
import java.util.List;

/**
* Implementation of tree based on {@link NodeStateBuilder}s. Each subtree
* has an associated node state builder which is used for building the new
* trees resulting from calling mutating methods.
*/
public class TreeImpl implements Tree {

    /** Underlying store */
    private final NodeStore store;

    /**
     * Underlying persistent state or {@code null} if this instance represents an
     * added tree
     */
    private final NodeState baseState;

    private final NodeStateBuilder builder;

    /** Listener for changes on this tree */
    private final Listener listener;

    /** Name of this tree */
    private String name;

    /** Parent of this tree */
    private TreeImpl parent;

    private TreeImpl(NodeStore store, NodeState baseState, NodeStateBuilder builder,
            TreeImpl parent, String name, Listener listener) {

        this.store = store;
        this.builder = builder;
        this.baseState = baseState;
        this.listener = listener;
        this.name = name;
        this.parent = parent;
    }

    /**
     * Create a new instance which represents the root of a tree.
     * @param store  underlying store to the tree
     * @param nodeStateBuilder  builder for the root
     * @param listener  change listener for the tree. May be {@code null} if
     *                  listening to changes is not needed.
     */
    TreeImpl(NodeStore store, NodeStateBuilder nodeStateBuilder, Listener listener) {
        this(store, nodeStateBuilder.getNodeState(), nodeStateBuilder, null, "", listener);
    }

    /**
     * Listener for changes on {@code Tree}s
     */
    interface Listener {

        /**
         * The child of the given {@code name} has been added to {@code tree}.
         * @param parent  parent to which a child was added
         * @param name  name of the added child
         */
        void addChild(TreeImpl parent, String name);

        /**
         * The child of the given {@code name} has been removed from {@code tree}
         * @param parent  parent from which a child was removed
         * @param name  name of the removed child
         */
        void removeChild(TreeImpl parent, String name);

        /**
         * The property of the given {@code name} and {@code value} has been set.
         * @param parent  parent on which the property was set.
         * @param name  name of the property
         * @param value  value of the property
         */
        void setProperty(TreeImpl parent, String name, CoreValue value);

        /**
         * The property of the given {@code name} and {@code values} has been set.
         * @param parent  parent on which the property was set.
         * @param name  name of the property
         * @param values  values of the property
         */
        void setProperty(TreeImpl parent, String name, List<CoreValue> values);

        /**
         * The property of the given {@code name} has been removed.
         * @param parent  parent on which the property was removed.
         * @param name  name of the property
         */
        void removeProperty(TreeImpl parent, String name);

        /**
         * The child with the given {@code name} has been moved.
         * @param sourceParent  parent from which the child was moved
         * @param sourceName  name of the moved child
         * @param moved  moved child
         */
        void move(TreeImpl sourceParent, String sourceName, TreeImpl moved);

        /**
         * The child with the given {@code name} been copied.
         * @param sourceParent  parent from which the child way copied
         * @param sourceName  name of the copied child
         * @param copied  copied child
         */
        void copy(TreeImpl sourceParent, String sourceName, TreeImpl copied);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getPath() {
        if (parent == null) {
            return name;
        }
        else {
            String path = parent.getPath();
            return path.isEmpty()
                    ? name
                    : path + '/' + name;
        }
    }

    @Override
    public Tree getParent() {
        return parent;
    }

    @Override
    public PropertyState getProperty(String name) {
        return getNodeState().getProperty(name);
    }

    @Override
    public Status getPropertyStatus(String name) {
        if (baseState == null) {
            // This instance is NEW...
            if (hasProperty(name)) {
                // ...so all children are new
                return Status.NEW;
            }
            else {
                // ...unless they don't exist.
                return null;
            }
        }
        else {
            if (hasProperty(name)) {
                // We have the property...
                if (baseState.getProperty(name) == null) {
                    // ...but didn't have it before. So its NEW.
                    return Status.NEW;
                }
                else {
                    // ... and did have it before. So...
                    PropertyState base = baseState.getProperty(name);
                    PropertyState head = getProperty(name);
                    if (base.equals(head)) {
                        // ...it's EXISTING if it hasn't changed
                        return Status.EXISTING;
                    }
                    else {
                        // ...and MODIFIED otherwise.
                        return Status.MODIFIED;
                    }
                }
            }
            else {
                // We don't have the property
                if (baseState.getProperty(name) == null) {
                    // ...and didn't have it before. So it doesn't exist.
                    return null;
                }
                else {
                    // ...and didn't have it before. So it's REMOVED
                    return Status.REMOVED;
                }
            }
        }
    }

    @Override
    public boolean hasProperty(String name) {
        return getNodeState().getProperty(name) != null;
    }

    @Override
    public long getPropertyCount() {
        return getNodeState().getPropertyCount();
    }

    @Override
    public Iterable<? extends PropertyState> getProperties() {
        return getNodeState().getProperties();
    }

    @Override
    public TreeImpl getChild(String name) {
        NodeStateBuilder childBuilder = builder.getChildBuilder(name);
        if (childBuilder == null) {
            return null;
        }
        else {
            NodeState childBaseState = baseState == null
                    ? null
                    : baseState.getChildNode(name);

            return new TreeImpl(store, childBaseState, childBuilder, this, name, listener);
        }
    }

    @Override
    public Status getChildStatus(String name) {
        if (baseState == null) {
            // This instance is NEW...
            if (hasChild(name)) {
                // ...so all children are new
                return Status.NEW;
            }
            else {
                // ...unless they don't exist.
                return null;
            }
        }
        else {
            if (hasChild(name)) {
                // We have the child...
                if (baseState.getChildNode(name) == null) {
                    // ...but didn't have it before. So its NEW.
                    return Status.NEW;
                }
                else {
                    // ... and did have it before. So...
                    if (isSame(baseState.getChildNode(name), getNodeState().getChildNode(name))) {
                        // ...it's EXISTING if it hasn't changed
                        return Status.EXISTING;
                    }
                    else {
                        // ...and MODIFIED otherwise.
                        return Status.MODIFIED;
                    }
                }
            }
            else {
                // We don't have the child
                if (baseState.getChildNode(name) == null) {
                    // ...and didn't have it before. So it doesn't exist.
                    return null;
                }
                else {
                    // ...and didn't have it before. So it's REMOVED
                    return Status.REMOVED;
                }
            }
        }
    }

    @Override
    public boolean hasChild(String name) {
        return getNodeState().getChildNode(name) != null;
    }

    @Override
    public long getChildrenCount() {
        return getNodeState().getChildNodeCount();
    }

    @Override
    public Iterable<Tree> getChildren() {
        return new Iterable<Tree>() {
            @Override
            public Iterator<Tree> iterator() {
                Iterator<? extends ChildNodeEntry> childEntries =
                        getNodeState().getChildNodeEntries().iterator();

                return Iterators.map(childEntries, new Function1<ChildNodeEntry, Tree>() {
                    @Override
                    public Tree apply(ChildNodeEntry entry) {
                        NodeStateBuilder childBuilder = builder.getChildBuilder(entry.getName());
                        return new TreeImpl(store, childBuilder.getNodeState(), childBuilder, TreeImpl.this, entry.getName(), listener);
                    }
                });
            }
        };
    }

    @Override
    public Tree addChild(String name) {
        builder.addNode(name);
        TreeImpl added = getChild(name);
        if (added != null) {
            listener.addChild(this, name);
        }
        return added;
    }

    @Override
    public boolean removeChild(String name) {
        boolean result = builder.removeNode(name);
        if (result) {
            listener.removeChild(this, name);
        }
        return result;
    }

    @Override
    public void setProperty(String name, CoreValue value) {
        builder.setProperty(name, value);
        if (listener != null) {
            listener.setProperty(this, name, value);
        }
    }

    @Override
    public void setProperty(String name, List<CoreValue> values) {
        builder.setProperty(name, values);
        if (listener != null) {
            listener.setProperty(this, name, values);
        }
    }

    @Override
    public void removeProperty(String name) {
        builder.removeProperty(name);
        if (listener != null) {
            listener.removeProperty(this, name);
        }
    }

    /**
     * Move this tree to the parent at {@code destParent} with the new name
     * {@code destName}.
     *
     * @param destParent  new parent for this tree
     * @param destName  new name for this tree
     * @return  {@code true} if successful, {@code false otherwise}. I.e.
     * when {@code destName} already exists at {@code destParent}
     */
    public boolean move(TreeImpl destParent, String destName) {
        boolean result = builder.moveTo(destParent.builder, destName);
        if (result) {
            TreeImpl oldParent = parent;
            String oldName = name;

            name = destName;
            parent = destParent;

            if (listener != null) {
                listener.move(oldParent, oldName, this);
            }
        }
        return result;
    }

    /**
     * Copy this tree to the parent at {@code destParent} with the name {@code destName}.
     *
     * @param destParent  parent for the copied tree
     * @param destName  name for the copied tree
     * @return  {@code true} if successful, {@code false otherwise}. I.e.
     * when {@code destName} already exists at {@code destParent}
     */
    public boolean copy(TreeImpl destParent, String destName) {
        boolean result = builder.copyTo(destParent.builder, destName);
        if (result) {
            if (listener != null) {
                listener.copy(parent, name, destParent.getChild(destName));
            }
            return true;
        }
        return result;
    }

    //------------------------------------------------------------< private >---

    private NodeState getNodeState() {
        return builder.getNodeState();
    }

    private boolean isSame(NodeState state1, NodeState state2) {
        final boolean[] isDirty = {false};
        store.compare(state1, state2, new NodeStateDiff() {
            @Override
            public void propertyAdded(PropertyState after) {
                isDirty[0] = true;
            }

            @Override
            public void propertyChanged(PropertyState before, PropertyState after) {
                isDirty[0] = true;
            }

            @Override
            public void propertyDeleted(PropertyState before) {
                isDirty[0] = true;
            }

            @Override
            public void childNodeAdded(String name, NodeState after) {
                isDirty[0] = true;
            }

            @Override
            public void childNodeChanged(String name, NodeState before, NodeState after) {
                // cut transitivity here
            }

            @Override
            public void childNodeDeleted(String name, NodeState before) {
                isDirty[0] = true;
            }
        });

        return !isDirty[0];
    }

}
TOP

Related Classes of org.apache.jackrabbit.oak.core.TreeImpl

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.