blob: 2102ebe92eff6ab07e336902fca0a0ac6d8da7f4 [file] [log] [blame]
/*
* 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.tree.impl;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static org.apache.jackrabbit.oak.api.Type.NAMES;
import static org.apache.jackrabbit.oak.plugins.tree.TreeConstants.OAK_CHILD_ORDER;
import java.util.List;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
import org.apache.jackrabbit.oak.plugins.tree.TreeConstants;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* {@code AbstractMutableTree} extends {@code AbstractTree} with implementations
* for most write methods of {@code Tree}. Furthermore it handles the ordering
* of siblings.
*/
public abstract class AbstractMutableTree extends AbstractTree {
@Override
public boolean remove() {
String name = getName();
AbstractTree parent = getParentOrNull();
if (parent != null && parent.hasChild(name)) {
getNodeBuilder().remove();
NodeBuilder parentBuilder = parent.getNodeBuilder();
PropertyState order = parentBuilder.getProperty(OAK_CHILD_ORDER);
if (order != null) {
List<String> names = newArrayListWithCapacity(order.count());
for (String n : order.getValue(NAMES)) {
if (!n.equals(name)) {
names.add(n);
}
}
parentBuilder.setProperty(OAK_CHILD_ORDER, names, NAMES);
}
return true;
} else {
return false;
}
}
@NotNull
@Override
public Tree addChild(@NotNull String name) throws IllegalArgumentException {
checkArgument(!isHidden(name));
if (!hasChild(name)) {
NodeBuilder nodeBuilder = getNodeBuilder();
nodeBuilder.setChildNode(name);
PropertyState order = nodeBuilder.getProperty(OAK_CHILD_ORDER);
if (order != null) {
List<String> names = newArrayListWithCapacity(order.count() + 1);
for (String n : order.getValue(NAMES)) {
if (!n.equals(name)) {
names.add(n);
}
}
names.add(name);
nodeBuilder.setProperty(OAK_CHILD_ORDER, names, NAMES);
}
}
return createChild(name);
}
@Override
public void setOrderableChildren(boolean enable) {
if (enable) {
updateChildOrder(true);
} else {
getNodeBuilder().removeProperty(OAK_CHILD_ORDER);
}
}
/**
* Updates the child order to match any added or removed child nodes that
* are not yet reflected in the {@link TreeConstants#OAK_CHILD_ORDER}
* property. If the {@code force} flag is set, the child order is set
* in any case, otherwise only if the node already is orderable.
*
* @param force whether to add child order information if it doesn't exist
*/
protected void updateChildOrder(boolean force) {
if (force || hasOrderableChildren()) {
getNodeBuilder().setProperty(PropertyStates.createProperty(
OAK_CHILD_ORDER, getChildNames(), Type.NAMES));
}
}
@Override
public boolean orderBefore(@Nullable String name) {
String thisName = getName();
AbstractTree parent = getParentOrNull();
if (parent == null) {
return false; // root does not have siblings
} else if (thisName.equals(name)) {
return false; // same node
}
// perform the reorder
List<String> names = newArrayListWithCapacity(10000);
NodeBuilder builder = parent.getNodeBuilder();
boolean found = false;
// first try reordering based on the (potentially out-of-sync)
// child order property in the parent node
for (String n : builder.getNames(OAK_CHILD_ORDER)) {
if (n.equals(name) && parent.hasChild(name)) {
names.add(thisName);
found = true;
}
if (!n.equals(thisName)) {
names.add(n);
}
}
// if the target node name was not found in the parent's child order
// property, we need to fall back to recreating the child order list
if (!found) {
names.clear();
for (String n : parent.getChildNames()) {
if (n.equals(name)) {
names.add(thisName);
found = true;
}
if (!n.equals(thisName)) {
names.add(n);
}
}
}
if (name == null) {
names.add(thisName);
found = true;
}
if (found) {
builder.setProperty(OAK_CHILD_ORDER, names, NAMES);
return true;
} else {
// no such sibling (not existing or not accessible)
return false;
}
}
@Override
public void setProperty(@NotNull PropertyState property) {
checkArgument(!isHidden(checkNotNull(property).getName()));
getNodeBuilder().setProperty(property);
}
@Override
public <T> void setProperty(@NotNull String name, @NotNull T value) throws IllegalArgumentException {
checkArgument(!isHidden(checkNotNull(name)));
getNodeBuilder().setProperty(name, checkNotNull(value));
}
@Override
public <T> void setProperty(@NotNull String name, @NotNull T value, @NotNull Type<T> type)
throws IllegalArgumentException {
checkArgument(!isHidden(checkNotNull(name)));
getNodeBuilder().setProperty(name, checkNotNull(value), checkNotNull(type));
}
@Override
public void removeProperty(@NotNull String name) {
getNodeBuilder().removeProperty(checkNotNull(name));
}
}