All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.oak.core.AbstractTree Maven / Gradle / Ivy

There is a newer version: 1.64.0
Show newest version
/*
 * 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 static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.size;
import static com.google.common.collect.Iterables.transform;
import static org.apache.jackrabbit.oak.api.Tree.Status.EXISTING;
import static org.apache.jackrabbit.oak.api.Tree.Status.MODIFIED;
import static org.apache.jackrabbit.oak.api.Tree.Status.NEW;
import static org.apache.jackrabbit.oak.api.Type.NAME;
import static org.apache.jackrabbit.oak.spi.state.NodeStateUtils.isHidden;

import java.util.Iterator;

import javax.annotation.Nonnull;

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

import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;

/**
 * {@code AbstractTree} provides default implementations for most
 * read methods of {@code Tree}. Furthermore it handles the
 * {@link #setOrderableChildren(boolean) ordering} of child nodes
 * and hides internal items.
 */
public abstract class AbstractTree implements Tree {

    /**
     * Name of the internal property that contains the child order
     */
    public static final String OAK_CHILD_ORDER = ":childOrder";

    // TODO: make this configurable
    private static final String[] INTERNAL_NODE_NAMES = {
            IndexConstants.INDEX_CONTENT_NODE_NAME,
            NodeReferenceConstants.REF_NAME,
            NodeReferenceConstants.WEAK_REF_NAME,
            MicroKernel.CONFLICT_NAME};

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

    /**
     * The {@code NodeBuilder} for the underlying node state
     */
    protected NodeBuilder nodeBuilder;

    /**
     * Create a new {@code AbstractTree} instance
     * @param name  name of the tree
     * @param nodeBuilder  {@code NodeBuilder} for the underlying node state
     */
    protected AbstractTree(@Nonnull String name, @Nonnull NodeBuilder nodeBuilder) {
        this.name = checkNotNull(name);
        this.nodeBuilder = checkNotNull(nodeBuilder);
    }

    /**
     * @return  the underlying {@code NodeState} of this tree
     */
    @Nonnull
    public NodeState getNodeState() {
        return nodeBuilder.getNodeState();
    }

    /**
     * Factory method for creating child trees
     * @param name  name of the child tree
     * @return  child tree of this tree with the given {@code name}
     */
    @Nonnull
    protected abstract AbstractTree createChild(@Nonnull String name);

    /**
     * @return  {@code true} iff {@code getStatus() == Status.NEW}
     */
    protected boolean isNew() {
        return nodeBuilder.isNew();
    }

    /**
     * @return  {@code true} iff {@code getStatus() == Status.MODIFIED}
     */
    protected boolean isModified() {
        return nodeBuilder.isModified();
    }

    /**
     * @return {@code true} if this tree has orderable children;
     *         {@code false} otherwise.
     */
    protected boolean hasOrderableChildren() {
        return nodeBuilder.hasProperty(OAK_CHILD_ORDER);
    }

    /**
     * Returns the list of child names considering its ordering
     * when the {@link #OAK_CHILD_ORDER} property is set.
     *
     * @return the list of child names.
     */
    @Nonnull
    protected Iterable getChildNames() {
        if (hasOrderableChildren()) {
            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator() {
                        final PropertyState childOrder = nodeBuilder.getProperty(OAK_CHILD_ORDER);
                        int index;

                        @Override
                        public boolean hasNext() {
                            return index < childOrder.count();
                        }

                        @Override
                        public String next() {
                            return childOrder.getValue(NAME, index++);
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException();
                        }
                    };
                }
            };
        } else {
            return nodeBuilder.getChildNodeNames();
        }
    }

    //------------------------------------------------------------< Tree >---

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

    @Override
    public boolean isRoot() {
        return name.isEmpty();
    }

    @Override
    public String getPath() {
        if (isRoot()) {
            return "/";
        } else {
            StringBuilder sb = new StringBuilder();
            buildPath(sb);
            return sb.toString();
        }
    }

    private void buildPath(StringBuilder sb) {
        if (!isRoot()) {
            getParent().buildPath(sb);
            sb.append('/').append(name);
        }
    }

    @Override
    public Status getStatus() {
        if (nodeBuilder.isNew()) {
            return NEW;
        } else if (nodeBuilder.isModified()) {
            return MODIFIED;
        } else {
            return EXISTING;
        }
    }

    @Override
    public boolean exists() {
        return !isHidden(name) && nodeBuilder.exists();
    }

    @Override
    public abstract AbstractTree getParent();

    @Override
    public PropertyState getProperty(String name) {
        return !isHidden(checkNotNull(name))
            ? nodeBuilder.getProperty(name)
            : null;
    }

    @Override
    public boolean hasProperty(String name) {
        return (!isHidden(checkNotNull(name))) && nodeBuilder.hasProperty(name);
    }

    @Override
    public long getPropertyCount() {
        return size(getProperties());
    }

    @Override
    public Status getPropertyStatus(@Nonnull String name) {
        if (!hasProperty(name)) {
            return null;
        } else if (nodeBuilder.isNew(name)) {
            return NEW;
        } else if (nodeBuilder.isReplaced(name)) {
            return MODIFIED;
        } else {
            return EXISTING;
        }
    }

    @Override
    public Iterable getProperties() {
        return filter(nodeBuilder.getProperties(),
            new Predicate() {
                @Override
                public boolean apply(PropertyState propertyState) {
                    return !isHidden(propertyState.getName());
                }
            });
    }

    @Override
    public boolean hasChild(String name) {
        return createChild(checkNotNull(name)).exists();
    }

    @Override
    public long getChildrenCount(long max) {
        if (max + INTERNAL_NODE_NAMES.length < 0) {
            // avoid overflow (if max is near Long.MAX_VALUE)
            max = Long.MAX_VALUE;
        } else {
            // fetch a few more
            max += INTERNAL_NODE_NAMES.length;
        }
        long count = nodeBuilder.getChildNodeCount(max);
        if (count > 0) {
            for (String name : INTERNAL_NODE_NAMES) {
                if (nodeBuilder.hasChildNode(name)) {
                    count--;
                }
            }
        }
        return count;
    }

    @Override
    public Iterable getChildren() {
        return transform(
                filter(getChildNames(), new Predicate() {
                    @Override
                    public boolean apply(String name) {
                        return !isHidden(name);
                    }
                }),
                new Function() {
                    @Override
                    public Tree apply(String name) {
                        return createChild(name);
                    }
                });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy