Package org.apache.jackrabbit.oak.jcr

Source Code of org.apache.jackrabbit.oak.jcr.NodeDelegate

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemNotFoundException;

import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.CoreValueFactory;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Tree.Status;
import org.apache.jackrabbit.oak.commons.PathUtils;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;

/**
* {@code NodeDelegate} serve as internal representations of {@code Node}s.
* Most methods of this class throw an {@code InvalidItemStateException}
* exception if the instance is stale. An instance is stale if the underlying
* items does not exist anymore.
*/
public class NodeDelegate extends ItemDelegate {

    /**
     * The underlying {@link Tree} instance. In order to ensure the instance
     * is up to date, this field <em>should not be accessed directly</em> but
     * rather the {@link #getTree()} method should be used.
     */
    private Tree tree;

    public NodeDelegate(SessionDelegate sessionDelegate, Tree tree) {
        super(sessionDelegate);
        this.tree = tree;
    }

    @Override
    public String getName() throws InvalidItemStateException {
        return getTree().getName();
    }

    @Override
    public String getPath() throws InvalidItemStateException {
        return '/' + getTree().getPath();
    }

    @Override
    public NodeDelegate getParent() throws InvalidItemStateException {
        Tree parent = getTree().getParent();
        return parent == null ? null : new NodeDelegate(sessionDelegate, parent);
    }

    @Override
    public boolean isStale() {
        resolve();
        return tree == null;
    }

    @Override
    public Status getStatus() throws InvalidItemStateException {
        return getTree().getStatus();
    }

    @Override
    public String toString() {
        // don't disturb the state: avoid calling getTree()
        return "NodeDelegate[/" + tree.getPath() + ']';
    }

    @Nonnull
    public String getIdentifier() throws InvalidItemStateException {
        PropertyDelegate pd = getProperty(JcrConstants.JCR_UUID);
        if (pd == null) {
            // TODO delegated to the OAK-API to calculate the identifier
            // TODO consisting of closest referenceable parent and a relative path
            // TODO irrespective of the accessibility of the parent node(s)
            return getPath();
        } else {
            return pd.getValue().toString();
        }
    }

    /**
     * Determine whether this is the root node
     * @return  {@code true} iff this is the root node
     */
    public boolean isRoot() throws InvalidItemStateException {
        return getTree().isRoot();
    }

    /**
     * Get the number of properties of the node
     * @return  number of properties of the node
     */
    public long getPropertyCount() throws InvalidItemStateException {
        // TODO: Exclude "invisible" internal properties (OAK-182)
        return getTree().getPropertyCount();
    }

    /**
     * Get a property
     * @param relPath  oak path
     * @return  property at the path given by {@code relPath} or {@code null} if
     * no such property exists
     */
    @CheckForNull
    public PropertyDelegate getProperty(String relPath) throws InvalidItemStateException {
        Tree parent = getTree(PathUtils.getParentPath(relPath));
        if (parent == null) {
            return null;
        }

        String name = PathUtils.getName(relPath);
        PropertyState propertyState = parent.getProperty(name);
        return propertyState == null
            ? null
            : new PropertyDelegate(sessionDelegate, parent, propertyState);
    }

    /**
     * Get the properties of the node
     * @return  properties of the node
     */
    @Nonnull
    public Iterator<PropertyDelegate> getProperties() throws InvalidItemStateException {
        return propertyDelegateIterator(getTree().getProperties().iterator());
    }

    /**
     * Get the number of child nodes
     * @return  number of child nodes of the node
     */
    public long getChildCount() throws InvalidItemStateException {
        // TODO: Exclude "invisible" internal child nodes (OAK-182)
        return getTree().getChildrenCount();
    }

    /**
     * Get child node
     * @param relPath  oak path
     * @return  node at the path given by {@code relPath} or {@code null} if
     * no such node exists
     */
    @CheckForNull
    public NodeDelegate getChild(String relPath) throws InvalidItemStateException {
        Tree tree = getTree(relPath);
        return tree == null ? null : new NodeDelegate(sessionDelegate, tree);
    }

    /**
     * Returns an iterator for traversing all the children of this node.
     * If the node is orderable (there is an {@link PropertyState#OAK_CHILD_ORDER}
     * property) then the iterator will return child nodes in the specified
     * order. Otherwise the ordering of the iterator is undefined.
     *
     * @return child nodes of the node
     */
    @Nonnull
    public Iterator<NodeDelegate> getChildren() throws InvalidItemStateException {
        Tree tree = getTree();
        long count = tree.getChildrenCount();
        if (count == 0) {
            // Optimise the most common case
            return Collections.<NodeDelegate>emptySet().iterator();
        } else if (count == 1) {
            // Optimise another typical case
            Tree child = tree.getChildren().iterator().next();
            if (!child.getName().startsWith(":")) {
                NodeDelegate delegate = new NodeDelegate(sessionDelegate, child);
                return Collections.singleton(delegate).iterator();
            } else {
                return Collections.<NodeDelegate>emptySet().iterator();
            }
        } else {
            PropertyState order = tree.getProperty(PropertyState.OAK_CHILD_ORDER);
            if (order == null || !order.isArray()) {
                // No specified ordering
                return nodeDelegateIterator(tree.getChildren().iterator());
            } else {
                // Collect child nodes in the specified order
                final Map<String, NodeDelegate> ordered =
                        new LinkedHashMap<String, NodeDelegate>();

                for (CoreValue value : order.getValues()) {
                    String name = value.getString();
                    Tree child = tree.getChild(name);
                    if (child != null && !name.startsWith(":")) {
                        ordered.put(name, new NodeDelegate(sessionDelegate, child));
                    }
                }

                if (ordered.size() == count) {
                    // We have all the child nodes
                    return ordered.values().iterator();
                } else {
                    // The specified ordering didn't cover all the children,
                    // so return a combined iterator that first iterates
                    // through the ordered subset and then all the remaining
                    // children in an undefined order
                    Iterator<Tree> remaining = Iterators.filter(
                            tree.getChildren().iterator(),
                            new Predicate<Tree>() {
                                @Override
                                public boolean apply(Tree tree) {
                                    return !ordered.containsKey(tree.getName());
                                }
                            });
                    return Iterators.concat(
                            ordered.values().iterator(),
                            nodeDelegateIterator(remaining));
                }
            }
        }
    }

    public void orderBefore(String source, String target)
            throws ItemNotFoundException, InvalidItemStateException {
        Tree tree = getTree();
        if (tree.getChild(source) == null) {
            throw new ItemNotFoundException("Not a child: " + source);
        } else if (target != null && tree.getChild(target) == null) {
            throw new ItemNotFoundException("Not a child: " + target);
        } else {
            List<CoreValue> order = new ArrayList<CoreValue>();
            Set<String> added = new HashSet<String>();
            CoreValueFactory factory =
                    sessionDelegate.getContentSession().getCoreValueFactory();

            PropertyState property = tree.getProperty(PropertyState.OAK_CHILD_ORDER);
            if (property != null) {
                for (CoreValue value : property.getValues()) {
                    String name = value.getString();
                    if (!name.equals(source) && !added.contains(property)
                            && !name.startsWith(":")) {
                        if (name.equals(target)) {
                            order.add(factory.createValue(source));
                            added.add(source);
                        }
                        order.add(factory.createValue(name));
                        added.add(name);
                    }
                }
            }

            if (!added.contains(source)) {
                order.add(factory.createValue(source));
            }
            if (target != null && !added.contains(target)) {
                order.add(factory.createValue(source));
            }

            tree.setProperty(PropertyState.OAK_CHILD_ORDER, order);
        }
    }

    /**
     * Set a property
     * @param name  oak name
     * @param value
     * @return  the set property
     */
    @Nonnull
    public PropertyDelegate setProperty(String name, CoreValue value) throws InvalidItemStateException {
        PropertyState propertyState = getTree().setProperty(name, value);
        return new PropertyDelegate(sessionDelegate, getTree(), propertyState);
    }

    public void removeProperty(String name) throws InvalidItemStateException {
        getTree().removeProperty(name);
    }

    /**
     * Set a multi valued property
     * @param name  oak name
     * @param value
     * @return  the set property
     */
    @Nonnull
    public PropertyDelegate setProperty(String name, List<CoreValue> value) throws InvalidItemStateException {
        PropertyState propertyState = getTree().setProperty(name, value);
        return new PropertyDelegate(sessionDelegate, getTree(), propertyState);
    }

    /**
     * Add a child node
     * @param name  oak name
     * @return  the added node or {@code null} if such a node already exists
     */
    @CheckForNull
    public NodeDelegate addChild(String name) throws InvalidItemStateException {
        Tree tree = getTree();
        return tree.hasChild(name)
            ? null
            : new NodeDelegate(sessionDelegate, tree.addChild(name));
    }

    /**
     * Remove the node if not root. Does nothing otherwise
     */
    public void remove() throws InvalidItemStateException {
        getTree().remove();
    }

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

    private Tree getTree(String relPath) throws InvalidItemStateException {
        String absPath = PathUtils.concat(getPath(), relPath);
        return sessionDelegate.getTree(absPath);
    }

    private synchronized Tree getTree() throws InvalidItemStateException {
        resolve();
        if (tree == null) {
            throw new InvalidItemStateException("Node is stale");
        }

        return tree;
    }

    private synchronized void resolve() {
        if (tree != null) {
            tree = tree.getStatus() == Status.REMOVED
                ? null
                : sessionDelegate.getTree(tree.getPath());
        }
    }

    private Iterator<NodeDelegate> nodeDelegateIterator(
            Iterator<Tree> childNodeStates) {
        return Iterators.transform(
                Iterators.filter(childNodeStates, new Predicate<Tree>() {
                    @Override
                    public boolean apply(Tree tree) {
                        return !tree.getName().startsWith(":");
                    }
                }),
                new Function<Tree, NodeDelegate>() {
                    @Override
                    public NodeDelegate apply(Tree state) {
                        return new NodeDelegate(sessionDelegate, state);
                    }
                });
    }

    private Iterator<PropertyDelegate> propertyDelegateIterator(
            Iterator<? extends PropertyState> properties) {
        return Iterators.transform(
                Iterators.filter(properties, new Predicate<PropertyState>() {
                    @Override
                    public boolean apply(PropertyState property) {
                        return !property.getName().startsWith(":");
                    }
                }),
                new Function<PropertyState, PropertyDelegate>() {
                    @Override
                    public PropertyDelegate apply(PropertyState propertyState) {
                        return new PropertyDelegate(sessionDelegate, tree,
                                propertyState);
                    }
                });
    }
}
TOP

Related Classes of org.apache.jackrabbit.oak.jcr.NodeDelegate

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.