You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2012/10/25 10:56:13 UTC
svn commit: r1402028 - in
/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak:
kernel/KernelRootBuilder.java plugins/memory/MemoryNodeBuilder.java
plugins/memory/ModifiedNodeState.java spi/state/AbstractNodeState.java
Author: jukka
Date: Thu Oct 25 08:56:12 2012
New Revision: 1402028
URL: http://svn.apache.org/viewvc?rev=1402028&view=rev
Log:
OAK-170: Child node state builder
Extract ModifiedNodeState to a standalone class so we can leverage it also elsewhere.
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java?rev=1402028&r1=1402027&r2=1402028&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelRootBuilder.java Thu Oct 25 08:56:12 2012
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.oak.kernel;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState.collapse;
import java.util.Map;
import java.util.Set;
@@ -24,6 +25,7 @@ import java.util.Set;
import org.apache.jackrabbit.mk.api.MicroKernel;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import com.google.common.collect.Maps;
@@ -164,8 +166,8 @@ class KernelRootBuilder extends MemoryNo
//-------------------------------------------------------< private >--
private KernelNodeState getKernelBaseState(NodeState state) {
- if (state instanceof MutableNodeState) {
- state = ((MutableNodeState) state).getBaseState();
+ if (state instanceof ModifiedNodeState) {
+ state = collapse((ModifiedNodeState) state).getBaseState();
}
if (state instanceof KernelNodeState) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java?rev=1402028&r1=1402027&r2=1402028&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeBuilder.java Thu Oct 25 08:56:12 2012
@@ -17,33 +17,31 @@
package org.apache.jackrabbit.oak.plugins.memory;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
import javax.annotation.Nonnull;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
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 static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState.with;
+import static org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState.withNodes;
+import static org.apache.jackrabbit.oak.plugins.memory.ModifiedNodeState.withProperties;
/**
- * In-memory node state builder. The following two builder states are used
+ * In-memory node state builder.
+ * <p>
+ * TODO: The following description is somewhat out of date
+ * <p>
+ * The following two builder states are used
* to properly track uncommitted chances without relying on weak references
* or requiring hard references on the entire accessed subtree:
* <dl>
@@ -71,7 +69,7 @@ import static com.google.common.base.Pre
*/
public class MemoryNodeBuilder implements NodeBuilder {
- private static final NodeState NULL_STATE = new MemoryNodeState(
+ static final NodeState NULL_STATE = new MemoryNodeState(
ImmutableMap.<String, PropertyState>of(),
ImmutableMap.<String, NodeState>of());
@@ -248,11 +246,12 @@ public class MemoryNodeBuilder implement
@Override
public NodeState getNodeState() {
- NodeState state = read();
+ read();
if (writeState != null) {
- return new ModifiedNodeState(writeState);
+ return writeState.snapshot();
} else {
- return state;
+ assert baseState != null; // guaranteed by read()
+ return baseState;
}
}
@@ -337,9 +336,9 @@ public class MemoryNodeBuilder implement
MutableNodeState mstate = write();
if (mstate.base.getProperty(name) != null) {
- mstate.props.put(name, null);
+ mstate.properties.put(name, null);
} else {
- mstate.props.remove(name);
+ mstate.properties.remove(name);
}
updated();
@@ -349,7 +348,7 @@ public class MemoryNodeBuilder implement
@Override
public NodeBuilder setProperty(PropertyState property) {
MutableNodeState mstate = write();
- mstate.props.put(property.getName(), property);
+ mstate.properties.put(property.getName(), property);
updated();
return this;
}
@@ -398,79 +397,63 @@ public class MemoryNodeBuilder implement
}
/**
- * Filter for skipping property states with given names.
- */
- private static class SkipNamedProps implements Predicate<PropertyState> {
-
- private final Set<String> names;
-
- private SkipNamedProps(Set<String> names) {
- this.names = names;
- }
-
- @Override
- public boolean apply(PropertyState input) {
- return !names.contains(input.getName());
- }
-
- }
-
- /**
- * Filter for skipping child node states with given names.
- */
- private static class SkipNamedNodes implements Predicate<ChildNodeEntry> {
-
- private final Set<String> names;
-
- private SkipNamedNodes(Set<String> names) {
- this.names = names;
- }
-
- @Override
- public boolean apply(ChildNodeEntry input) {
- return !names.contains(input.getName());
- }
-
- }
-
- /**
* The <em>mutable</em> state being built. Instances of this class
* are never passed beyond the containing {@code MemoryNodeBuilder},
* so it's not a problem that we intentionally break the immutability
* assumption of the {@link NodeState} interface.
*/
- protected static class MutableNodeState extends AbstractNodeState {
+ private class MutableNodeState extends AbstractNodeState {
/**
* The immutable base state.
*/
- protected NodeState base;
+ private NodeState base;
/**
* Set of added, modified or removed ({@code null} value)
* property states.
*/
- protected final Map<String, PropertyState> props =
+ private final Map<String, PropertyState> properties =
Maps.newHashMap();
/**
* Set of added, modified or removed ({@code null} value)
* child nodes.
*/
- protected final Map<String, MutableNodeState> nodes =
+ private final Map<String, MutableNodeState> nodes =
Maps.newHashMap();
public MutableNodeState(NodeState base) {
if (base != null) {
this.base = base;
} else {
- this.base = NULL_STATE;
+ this.base = MemoryNodeBuilder.NULL_STATE;
+ }
+ }
+
+ public NodeState snapshot() {
+ Map<String, NodeState> nodes = Maps.newHashMap();
+ for (Map.Entry<String, MutableNodeState> entry : this.nodes.entrySet()) {
+ String name = entry.getKey();
+ MutableNodeState node = entry.getValue();
+ NodeState before = base.getChildNode(name);
+ if (node == null) {
+ if (before != null) {
+ nodes.put(name, null);
+ }
+ } else {
+ NodeState after = node.snapshot();
+ if (after != before) {
+ nodes.put(name, after);
+ }
+ }
}
+ return with(base, Maps.newHashMap(this.properties), nodes);
}
- private void reset(NodeState newBase) {
+ void reset(NodeState newBase) {
base = newBase;
- props.clear();
+ properties.clear();
Iterator<Map.Entry<String, MutableNodeState>> iterator =
nodes.entrySet().iterator();
@@ -486,293 +469,53 @@ public class MemoryNodeBuilder implement
}
}
- public NodeState getBaseState() {
- return base;
- }
-
//-----------------------------------------------------< NodeState >--
@Override
public long getPropertyCount() {
- long count = base.getPropertyCount();
-
- for (Map.Entry<String, PropertyState> entry : props.entrySet()) {
- if (base.getProperty(entry.getKey()) != null) {
- count--;
- }
- if (entry.getValue() != null) {
- count++;
- }
- }
-
- return count;
+ return withProperties(base, properties).getPropertyCount();
}
@Override
public PropertyState getProperty(String name) {
- PropertyState property = props.get(name);
- if (property != null || props.containsKey(name)) {
- return property;
- }
-
- return base.getProperty(name);
+ return withProperties(base, properties).getProperty(name);
}
- @Override
+ @Override @Nonnull
public Iterable<? extends PropertyState> getProperties() {
- if (props.isEmpty()) {
- return base.getProperties(); // shortcut
- } else {
- return internalGetProperties();
- }
- }
-
- protected Iterable<? extends PropertyState> internalGetProperties() {
- Predicate<PropertyState> unmodifiedFilter =
- new SkipNamedProps(ImmutableSet.copyOf(props.keySet()));
- Predicate<PropertyState> modifiedFilter = Predicates.notNull();
- return Iterables.concat(
- Iterables.filter(base.getProperties(), unmodifiedFilter),
- ImmutableList.copyOf(Collections2.filter(
- props.values(), modifiedFilter)));
+ Map<String, PropertyState> copy = Maps.newHashMap(properties);
+ return withProperties(base, copy).getProperties();
}
@Override
public long getChildNodeCount() {
- long count = base.getChildNodeCount();
-
- for (Map.Entry<String, MutableNodeState> entry : nodes.entrySet()) {
- if (base.getChildNode(entry.getKey()) != null) {
- count--;
- }
- if (entry.getValue() != null) {
- count++;
- }
- }
-
- return count;
+ return withNodes(base, nodes).getChildNodeCount();
}
@Override
public boolean hasChildNode(String name) {
- MutableNodeState node = nodes.get(name);
- if (node != null) {
- return true;
- } else if (nodes.containsKey(name)) {
- return false;
- }
-
- return base.hasChildNode(name);
+ return withNodes(base, nodes).hasChildNode(name);
}
@Override
public NodeState getChildNode(String name) {
- MutableNodeState node = nodes.get(name);
- if (node != null) {
- return node;
- } else if (nodes.containsKey(name)) {
- return null;
- }
-
- return base.getChildNode(name);
+ return withNodes(base, nodes).getChildNode(name); // mutable
}
- @Override
+ @Override @Nonnull
public Iterable<String> getChildNodeNames() {
- if (nodes.isEmpty()) {
- return base.getChildNodeNames(); // shortcut
- } else {
- return internalGetChildNodeNames();
- }
- }
-
- protected Iterable<String> internalGetChildNodeNames() {
- Iterable<String> unmodified = base.getChildNodeNames();
- Predicate<String> unmodifiedFilter = Predicates.not(Predicates.in(
- ImmutableSet.copyOf(nodes.keySet())));
- Set<String> modified = ImmutableSet.copyOf(
- Maps.filterValues(nodes, Predicates.notNull()).keySet());
- return Iterables.concat(
- Iterables.filter(unmodified, unmodifiedFilter),
- modified);
- }
-
- @Override
- public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
- if (nodes.isEmpty()) {
- return base.getChildNodeEntries(); // shortcut
- } else {
- return internalGetChildNodeEntries();
- }
- }
-
- protected Iterable<? extends ChildNodeEntry> internalGetChildNodeEntries() {
- Iterable<? extends ChildNodeEntry> unmodified =
- base.getChildNodeEntries();
- Predicate<ChildNodeEntry> unmodifiedFilter =
- new SkipNamedNodes(ImmutableSet.copyOf(nodes.keySet()));
-
- List<ChildNodeEntry> modified = Lists.newArrayList();
- for (Map.Entry<String, MutableNodeState> entry : nodes.entrySet()) {
- MutableNodeState cstate = entry.getValue();
- if (cstate != null) {
- modified.add(new MemoryChildNodeEntry(
- entry.getKey(),
- new ModifiedNodeState(cstate)));
- }
- }
-
- return Iterables.concat(
- Iterables.filter(unmodified, unmodifiedFilter),
- modified);
+ Map<String, MutableNodeState> copy = Maps.newHashMap(nodes);
+ return withNodes(base, copy).getChildNodeNames();
}
@Override
- public NodeBuilder builder() {
- return new ModifiedNodeState(this).builder();
- }
-
- /**
- * Since we keep track of an explicit base node state for a
- * {@link ModifiedNodeState} instance, we can do this in two steps:
- * first compare the base states to each other (often a fast operation),
- * ignoring all changed properties and child nodes for which we have
- * further modifications, and then compare all the modified properties
- * and child nodes to those in the given base state.
- */
- @Override
- public void compareAgainstBaseState(
- NodeState base, final NodeStateDiff diff) {
- this.base.compareAgainstBaseState(base, new NodeStateDiff() {
- @Override
- public void propertyAdded(PropertyState after) {
- if (!props.containsKey(after.getName())) {
- diff.propertyAdded(after);
- }
- }
- @Override
- public void propertyChanged(
- PropertyState before, PropertyState after) {
- if (!props.containsKey(before.getName())) {
- diff.propertyChanged(before, after);
- }
- }
- @Override
- public void propertyDeleted(PropertyState before) {
- if (!props.containsKey(before.getName())) {
- diff.propertyDeleted(before);
- }
- }
- @Override
- public void childNodeAdded(String name, NodeState after) {
- if (!nodes.containsKey(name)) {
- diff.childNodeAdded(name, after);
- }
- }
- @Override
- public void childNodeChanged(String name, NodeState before, NodeState after) {
- if (!nodes.containsKey(name)) {
- diff.childNodeChanged(name, before, after);
- }
- }
- @Override
- public void childNodeDeleted(String name, NodeState before) {
- if (!nodes.containsKey(name)) {
- diff.childNodeDeleted(name, before);
- }
- }
- });
-
- for (Map.Entry<String, PropertyState> entry : props.entrySet()) {
- PropertyState before = base.getProperty(entry.getKey());
- PropertyState after = entry.getValue();
- if (before == null && after == null) {
- // do nothing
- } else if (after == null) {
- diff.propertyDeleted(before);
- } else if (before == null) {
- diff.propertyAdded(after);
- } else if (!before.equals(after)) {
- diff.propertyChanged(before, after);
- }
- }
-
- for (Map.Entry<String, MutableNodeState> entry : nodes.entrySet()) {
- String name = entry.getKey();
- NodeState before = base.getChildNode(name);
- NodeState after = entry.getValue();
- if (before == null && after == null) {
- // do nothing
- } else if (after == null) {
- diff.childNodeDeleted(name, before);
- } else if (before == null) {
- diff.childNodeAdded(name, after);
- } else if (!before.equals(after)) {
- diff.childNodeChanged(name, before, after);
- }
- }
+ public void compareAgainstBaseState(NodeState base, NodeStateDiff diff) {
+ with(this.base, properties, nodes).compareAgainstBaseState(base, diff);
}
- }
-
- /**
- * Immutable snapshot of a mutable node state.
- */
- protected static class ModifiedNodeState extends MutableNodeState {
-
- public ModifiedNodeState(MutableNodeState mstate) {
- super(mstate.base);
- props.putAll(mstate.props);
- for (Map.Entry<String, MutableNodeState> entry
- : mstate.nodes.entrySet()) {
- String name = entry.getKey();
- MutableNodeState node = entry.getValue();
- if (node != null) {
- nodes.put(name, new ModifiedNodeState(node));
- } else {
- nodes.put(name, null);
- }
- }
- }
-
- @Override
+ @Override @Nonnull
public NodeBuilder builder() {
- return new MemoryNodeBuilder(this);
- }
-
- //----------------------------------------------< MutableNodeState >--
-
- @Override
- protected Iterable<? extends PropertyState> internalGetProperties() {
- Predicate<PropertyState> unmodifiedFilter =
- new SkipNamedProps(props.keySet());
- Predicate<PropertyState> modifiedFilter = Predicates.notNull();
- return Iterables.concat(
- Iterables.filter(base.getProperties(), unmodifiedFilter),
- Collections2.filter(props.values(), modifiedFilter));
- }
-
- @Override
- protected Iterable<String> internalGetChildNodeNames() {
- Iterable<String> unmodified = base.getChildNodeNames();
- Predicate<String> unmodifiedFilter =
- Predicates.not(Predicates.in(nodes.keySet()));
- return Iterables.concat(
- Iterables.filter(unmodified, unmodifiedFilter),
- Maps.filterValues(nodes, Predicates.notNull()).keySet());
- }
-
- @Override
- protected Iterable<? extends ChildNodeEntry> internalGetChildNodeEntries() {
- Iterable<? extends ChildNodeEntry> unmodified =
- base.getChildNodeEntries();
- Predicate<ChildNodeEntry> unmodifiedFilter =
- new SkipNamedNodes(nodes.keySet());
- Map<String, MutableNodeState> modified =
- Maps.filterValues(nodes, Predicates.notNull());
- return Iterables.concat(
- Iterables.filter(unmodified, unmodifiedFilter),
- MemoryChildNodeEntry.iterable(modified.entrySet()));
+ throw new UnsupportedOperationException();
}
}
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java?rev=1402028&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ModifiedNodeState.java Thu Oct 25 08:56:12 2012
@@ -0,0 +1,340 @@
+/*
+ * 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 org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry.iterable;
+
+import java.util.Map;
+
+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.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+/**
+ * Immutable snapshot of a mutable node state.
+ */
+public class ModifiedNodeState extends AbstractNodeState {
+
+ public static NodeState withProperties(
+ NodeState base, Map<String, ? extends PropertyState> properties) {
+ if (properties.isEmpty()) {
+ return base;
+ } else {
+ return new ModifiedNodeState(
+ base, properties, ImmutableMap.<String, NodeState>of());
+ }
+ }
+
+ public static NodeState withNodes(
+ NodeState base, Map<String, ? extends NodeState> nodes) {
+ if (nodes.isEmpty()) {
+ return base;
+ } else {
+ return new ModifiedNodeState(
+ base, ImmutableMap.<String, PropertyState>of(), nodes);
+ }
+ }
+
+ public static NodeState with(
+ NodeState base,
+ Map<String, ? extends PropertyState> properties,
+ Map<String, ? extends NodeState> nodes) {
+ if (properties.isEmpty() && nodes.isEmpty()) {
+ return base;
+ } else {
+ return new ModifiedNodeState(base, properties, nodes);
+ }
+ }
+
+ public static ModifiedNodeState collapse(ModifiedNodeState state) {
+ NodeState base = state.getBaseState();
+ if (base instanceof ModifiedNodeState) {
+ ModifiedNodeState mbase = collapse((ModifiedNodeState) base);
+
+ Map<String, PropertyState> properties =
+ Maps.newHashMap(mbase.properties);
+ properties.putAll(state.properties);
+
+ Map<String, NodeState> nodes =
+ Maps.newHashMap(mbase.nodes);
+ nodes.putAll(state.nodes);
+
+ return new ModifiedNodeState(
+ mbase.getBaseState(), properties, nodes);
+ } else {
+ return state;
+ }
+ }
+
+ /**
+ * The base state.
+ */
+ private final NodeState base;
+
+ /**
+ * Set of added, modified or removed ({@code null} value)
+ * property states.
+ */
+ private final Map<String, ? extends PropertyState> properties;
+
+ /**
+ * Set of added, modified or removed ({@code null} value)
+ * child nodes.
+ */
+ private final Map<String, ? extends NodeState> nodes;
+
+ public ModifiedNodeState(
+ @Nonnull NodeState base,
+ @Nonnull Map<String, ? extends PropertyState> properties,
+ @Nonnull Map<String, ? extends NodeState> nodes) {
+ this.base = checkNotNull(base);
+ this.properties = checkNotNull(properties);
+ this.nodes = checkNotNull(nodes);
+ }
+
+ public ModifiedNodeState(
+ @Nonnull NodeState base,
+ @Nonnull Map<String, ? extends PropertyState> properties) {
+ this(base, properties, ImmutableMap.<String, NodeState>of());
+ }
+
+ @Nonnull
+ public NodeState getBaseState() {
+ return base;
+ }
+
+ //---------------------------------------------------------< NodeState >--
+
+ @Override
+ public NodeBuilder builder() {
+ return new MemoryNodeBuilder(this);
+ }
+
+ @Override
+ public long getPropertyCount() {
+ long count = base.getPropertyCount();
+
+ for (Map.Entry<String, ? extends PropertyState> entry : properties.entrySet()) {
+ if (base.getProperty(entry.getKey()) != null) {
+ count--;
+ }
+ if (entry.getValue() != null) {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ @Override
+ public PropertyState getProperty(String name) {
+ PropertyState property = properties.get(name);
+ if (property != null) {
+ return property;
+ } else if (properties.containsKey(name)) {
+ return null; // removed
+ } else {
+ return base.getProperty(name);
+ }
+ }
+
+ @Override
+ public Iterable<? extends PropertyState> getProperties() {
+ if (properties.isEmpty()) {
+ return base.getProperties(); // shortcut
+ } else {
+ Predicate<PropertyState> filter = new Predicate<PropertyState>() {
+ @Override
+ public boolean apply(PropertyState input) {
+ return !properties.containsKey(input.getName());
+ }
+ };
+ return concat(
+ filter(base.getProperties(), filter),
+ filter(properties.values(), notNull()));
+ }
+ }
+
+ @Override
+ public long getChildNodeCount() {
+ long count = base.getChildNodeCount();
+
+ for (Map.Entry<String, ? extends NodeState> entry : nodes.entrySet()) {
+ if (base.getChildNode(entry.getKey()) != null) {
+ count--;
+ }
+ if (entry.getValue() != null) {
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ @Override
+ public boolean hasChildNode(String name) {
+ NodeState child = nodes.get(name);
+ if (child != null) {
+ return true;
+ } else if (nodes.containsKey(name)) {
+ return false; // removed
+ } else {
+ return base.hasChildNode(name);
+ }
+ }
+
+ @Override
+ public NodeState getChildNode(String name) {
+ NodeState child = nodes.get(name);
+ if (child != null) {
+ return child;
+ } else if (nodes.containsKey(name)) {
+ return null; // removed
+ } else {
+ return base.getChildNode(name);
+ }
+ }
+
+ @Override
+ public Iterable<String> getChildNodeNames() {
+ if (nodes.isEmpty()) {
+ return base.getChildNodeNames(); // shortcut
+ } else {
+ return concat(
+ filter(base.getChildNodeNames(), not(in(nodes.keySet()))),
+ filterValues(nodes, notNull()).keySet());
+ }
+ }
+
+ @Override
+ public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+ if (nodes.isEmpty()) {
+ return base.getChildNodeEntries(); // shortcut
+ } else {
+ Predicate<ChildNodeEntry> filter = new Predicate<ChildNodeEntry>() {
+ @Override
+ public boolean apply(ChildNodeEntry input) {
+ return !nodes.containsKey(input.getName());
+ }
+ };
+ return concat(
+ filter(base.getChildNodeEntries(), filter),
+ iterable(filterValues(nodes, notNull()).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 the base states to each other (often a fast operation),
+ * ignoring all changed properties and child nodes for which we have
+ * further modifications, and then compare all the modified properties
+ * and child nodes to those in the given base state.
+ */
+ @Override
+ public void compareAgainstBaseState(
+ NodeState base, final NodeStateDiff diff) {
+ if (this.base != base) {
+ this.base.compareAgainstBaseState(base, new NodeStateDiff() {
+ @Override
+ public void propertyAdded(PropertyState after) {
+ if (!properties.containsKey(after.getName())) {
+ diff.propertyAdded(after);
+ }
+ }
+ @Override
+ public void propertyChanged(
+ PropertyState before, PropertyState after) {
+ if (!properties.containsKey(before.getName())) {
+ diff.propertyChanged(before, after);
+ }
+ }
+ @Override
+ public void propertyDeleted(PropertyState before) {
+ if (!properties.containsKey(before.getName())) {
+ diff.propertyDeleted(before);
+ }
+ }
+ @Override
+ public void childNodeAdded(String name, NodeState after) {
+ if (!nodes.containsKey(name)) {
+ diff.childNodeAdded(name, after);
+ }
+ }
+ @Override
+ public void childNodeChanged(String name, NodeState before, NodeState after) {
+ if (!nodes.containsKey(name)) {
+ diff.childNodeChanged(name, before, after);
+ }
+ }
+ @Override
+ public void childNodeDeleted(String name, NodeState before) {
+ if (!nodes.containsKey(name)) {
+ diff.childNodeDeleted(name, before);
+ }
+ }
+ });
+ }
+
+ for (Map.Entry<String, ? extends PropertyState> entry : properties.entrySet()) {
+ PropertyState before = base.getProperty(entry.getKey());
+ PropertyState after = entry.getValue();
+ if (before == null && after == null) {
+ // do nothing
+ } else if (after == null) {
+ diff.propertyDeleted(before);
+ } else if (before == null) {
+ diff.propertyAdded(after);
+ } else if (!before.equals(after)) {
+ diff.propertyChanged(before, after);
+ }
+ }
+
+ for (Map.Entry<String, ? extends NodeState> entry : nodes.entrySet()) {
+ String name = entry.getKey();
+ NodeState before = base.getChildNode(name);
+ NodeState after = entry.getValue();
+ if (before == null && after == null) {
+ // do nothing
+ } else if (after == null) {
+ diff.childNodeDeleted(name, before);
+ } else if (before == null) {
+ diff.childNodeAdded(name, after);
+ } else if (!before.equals(after)) {
+ diff.childNodeChanged(name, before, after);
+ }
+ }
+ }
+
+}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java?rev=1402028&r1=1402027&r2=1402028&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/AbstractNodeState.java Thu Oct 25 08:56:12 2012
@@ -26,6 +26,8 @@ import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.Nonnull;
+
/**
* Abstract base class for {@link NodeState} implementations.
* This base class contains default implementations of the
@@ -88,6 +90,27 @@ public abstract class AbstractNodeState
});
}
+ @Override
+ public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+ return Iterables.transform(
+ getChildNodeNames(),
+ new Function<String, ChildNodeEntry>() {
+ @Override
+ public ChildNodeEntry apply(final String input) {
+ return new AbstractChildNodeEntry() {
+ @Override @Nonnull
+ public String getName() {
+ return input;
+ }
+ @Override @Nonnull
+ public NodeState getNodeState() {
+ return getChildNode(input);
+ }
+ };
+ }
+ });
+ }
+
/**
* Generic default comparison algorithm that simply walks through the
* property and child node lists of the given base state and compares