Package org.apache.jackrabbit.oak.plugins.memory

Source Code of org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState

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

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Maps.filterValues;
import static com.google.common.collect.Maps.newHashMap;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry.iterable;

import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Nonnull;

import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;

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

/**
* Immutable snapshot of a mutable node state.
*/
public class ModifiedNodeState extends AbstractNodeState {

    /**
     * Unwraps the given {@code NodeState} instance into the given internals
     * of a {@link MutableNodeState} instance that is being created or reset.
     * <p>
     * If the given base state is a {@code ModifiedNodeState} instance,
     * then the contained modifications are applied to the given properties
     * property and child node maps and the contained base state is returned
     * for use as the base state of the {@link MutableNodeState} instance.
     * <p>
     * If the given base state is not a {@code ModifiedNodeState}, then
     * the given property and child node maps are simply reset and the given
     * base state is returned as-is for use as the base state of the
     * {@link MutableNodeState} instance.
     *
     * @param base new base state
     * @param properties {@link MutableNodeState} property map
     * @param nodes {@link MutableNodeState} child node map
     * @return new {@link MutableNodeState} base state
     */
    static NodeState unwrap(
            @Nonnull NodeState base,
            @Nonnull Map<String, PropertyState> properties,
            @Nonnull Map<String, MutableNodeState> nodes) {
        properties.clear();
        for (Entry<String, MutableNodeState> entry : nodes.entrySet()) {
            entry.getValue().reset(base.getChildNode(entry.getKey()));
        }

        if (base instanceof ModifiedNodeState) {
            ModifiedNodeState modified = (ModifiedNodeState) base;

            properties.putAll(modified.properties);
            for (Entry<String, NodeState> entry : modified.nodes.entrySet()) {
                String name = entry.getKey();
                if (!nodes.containsKey(name)) {
                    nodes.put(name, new MutableNodeState(entry.getValue()));
                }
            }

            return modified.base;
        } else {
            return base;
        }
    }


    static long getPropertyCount(
            NodeState base, Map<String, PropertyState> properties) {
        long count = 0;
        if (base.exists()) {
            count = base.getPropertyCount();
            for (Entry<String, PropertyState> entry : properties.entrySet()) {
                if (base.hasProperty(entry.getKey())) {
                    count--;
                }
                if (entry.getValue() != null) {
                    count++;
                }
            }
        }
        return count;
    }

    static boolean hasProperty(
            NodeState base, Map<String, PropertyState> properties,
            String name) {
        if (properties.containsKey(name)) {
            return properties.get(name) != null;
        } else {
            return base.hasProperty(name);
        }
    }

    static PropertyState getProperty(
            NodeState base, Map<String, PropertyState> properties,
            String name) {
        PropertyState property = properties.get(name);
        if (property == null && !properties.containsKey(name)) {
            property = base.getProperty(name);
        }
        return property;
    }

    static Iterable<? extends PropertyState> getProperties(
            NodeState base, Map<String, PropertyState> properties,
            boolean copy) {
        if (!base.exists()) {
            return emptyList();
        } else if (properties.isEmpty()) {
            return base.getProperties(); // shortcut
        } else {
            if (copy) {
                properties = newHashMap(properties);
            }
            Predicate<PropertyState> predicate = Predicates.compose(
                    not(in(properties.keySet())), PropertyState.GET_NAME);
            return concat(
                    filter(base.getProperties(), predicate),
                    filter(properties.values(), notNull()));
        }
    }

    static long getChildNodeCount(
            NodeState base, Map<String, ? extends NodeState> nodes) {
        long count = 0;
        if (base.exists()) {
            count = base.getChildNodeCount();
            for (Entry<String, ? extends NodeState> entry
                    : nodes.entrySet()) {
                if (base.hasChildNode(entry.getKey())) {
                    count--;
                }
                if (entry.getValue().exists()) {
                    count++;
                }
            }
        }
        return count;
    }

    static Iterable<String> getChildNodeNames(
            NodeState base, Map<String, ? extends NodeState> nodes,
            boolean copy) {
        if (!base.exists()) {
            return emptyList();
        } else if (nodes.isEmpty()) {
            return base.getChildNodeNames(); // shortcut
        } else {
            if (copy) {
                nodes = newHashMap(nodes);
            }
            return concat(
                    filter(base.getChildNodeNames(), not(in(nodes.keySet()))),
                    filterValues(nodes, NodeState.EXISTS).keySet());
        }
    }

    /**
     * The base state.
     */
    private final NodeState base;

    /**
     * Set of added, modified or removed ({@code null} value)
     * property states.
     */
    private final Map<String, PropertyState> properties;

    /**
     * Set of added, modified or removed (non-existent value)
     * child nodes.
     */
    private final Map<String, NodeState> nodes;

    /**
     * Creates an immutable snapshot of the given internal state of a
     * {@link MutableNodeState} instance.
     *
     * @param base base state
     * @param properties current property modifications
     * @param nodes current child node modifications
     */
    ModifiedNodeState(
            @Nonnull NodeState base,
            @Nonnull Map<String, PropertyState> properties,
            @Nonnull Map<String, MutableNodeState> nodes) {
        this.base = checkNotNull(base);

        if (checkNotNull(properties).isEmpty()) {
            this.properties = emptyMap();
        } else {
            this.properties = newHashMap(properties);
        }

        if (checkNotNull(nodes).isEmpty()) {
            this.nodes = emptyMap();
        } else {
            this.nodes = newHashMap();
            for (Entry<String, MutableNodeState> entry : nodes.entrySet()) {
                this.nodes.put(entry.getKey(), entry.getValue().snapshot());
            }
        }
    }

    @Nonnull
    public NodeState getBaseState() {
        return base;
    }

    //---------------------------------------------------------< NodeState >--

    @Override
    public NodeBuilder builder() {
        return new MemoryNodeBuilder(this);
    }

    @Override
    public boolean exists() {
        return base.exists();
    }

    @Override
    public long getPropertyCount() {
        return getPropertyCount(base, properties);
    }

    @Override
    public boolean hasProperty(String name) {
        return hasProperty(base, properties, name);
    }

    @Override
    public PropertyState getProperty(String name) {
        return getProperty(base, properties, name);
    }

    @Override
    public Iterable<? extends PropertyState> getProperties() {
        return getProperties(base, properties, false);
    }

    @Override
    public long getChildNodeCount() {
        return getChildNodeCount(base, nodes);
    }

    @Override
    public NodeState getChildNode(String name) {
        // checkArgument(!checkNotNull(name).isEmpty());  // TODO: should be caught earlier
        NodeState child = nodes.get(name);
        if (child == null) {
            child = base.getChildNode(name);
        }
        return child;
    }

    @Override
    public Iterable<String> getChildNodeNames() {
        return getChildNodeNames(base, nodes, false);
    }

    @Override
    public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
        if (!base.exists()) {
            return emptyList();
        } else if (nodes.isEmpty()) {
            return base.getChildNodeEntries(); // shortcut
        } else {
            Predicate<ChildNodeEntry> predicate = Predicates.compose(
                    not(in(nodes.keySet())), ChildNodeEntry.GET_NAME);
            return concat(
                    filter(base.getChildNodeEntries(), predicate),
                    iterable(filterValues(nodes, NodeState.EXISTS).entrySet()));
        }
    }

    /**
     * Since we keep track of an explicit base node state for a
     * {@link ModifiedNodeState} instance, we can do this in two steps:
     * first compare all the modified properties and child nodes to those
     * of the given base state, and then compare the base states to each
     * other, ignoring all changed properties and child nodes that were
     * already covered earlier.
     */
    @Override
    public boolean compareAgainstBaseState(
            NodeState base, final NodeStateDiff diff) {
        for (Map.Entry<String, PropertyState> entry : properties.entrySet()) {
            PropertyState before = base.getProperty(entry.getKey());
            PropertyState after = entry.getValue();
            if (after == null) {
                if (before != null && !diff.propertyDeleted(before)) {
                    return false;
                }
            } else if (before == null) {
                if (!diff.propertyAdded(after)) {
                    return false;
                }
            } else if (!before.equals(after)
                    && !diff.propertyChanged(before, after)) {
                return false;
            }
        }

        for (Map.Entry<String, NodeState> entry : nodes.entrySet()) {
            String name = entry.getKey();
            NodeState before = base.getChildNode(name);
            NodeState after = entry.getValue();
            if (!after.exists()) {
                if (before.exists() && !diff.childNodeDeleted(name, before)) {
                    return false;
                }
            } else if (!before.exists()) {
                if (!diff.childNodeAdded(name, after)) {
                    return false;
                }
            } else if (!before.equals(after)
                    && !diff.childNodeChanged(name, before, after)) {
                return false;
            }
        }

        return this.base.compareAgainstBaseState(base, new NodeStateDiff() {
            @Override
            public boolean propertyAdded(PropertyState after) {
                return properties.containsKey(after.getName())
                        || diff.propertyAdded(after);
            }
            @Override
            public boolean propertyChanged(
                    PropertyState before, PropertyState after) {
                return properties.containsKey(before.getName())
                        || diff.propertyChanged(before, after);
            }
            @Override
            public boolean propertyDeleted(PropertyState before) {
                return properties.containsKey(before.getName())
                        || diff.propertyDeleted(before);
            }
            @Override
            public boolean childNodeAdded(String name, NodeState after) {
                return nodes.containsKey(name)
                        || diff.childNodeAdded(name, after);
            }
            @Override
            public boolean childNodeChanged(String name, NodeState before, NodeState after) {
                return nodes.containsKey(name)
                        || diff.childNodeChanged(name, before, after);
            }
            @Override
            public boolean childNodeDeleted(String name, NodeState before) {
                return nodes.containsKey(name)
                        || diff.childNodeDeleted(name, before);
            }
        });
    }

}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState

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.