/*
* 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.mk.index;
import java.util.Arrays;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.oak.commons.ArrayUtils;
import org.apache.jackrabbit.oak.commons.PathUtils;
/**
* An index node page.
*/
class BTreeNode extends BTreePage {
private String[] children;
BTreeNode(BTree tree, BTreeNode parent, String name, String[] keys, String[] values, String[] children) {
super(tree, parent, name, keys, values);
this.children = children;
}
String getNextChildPath() {
int max = 0;
for (String c : children) {
int x = Integer.parseInt(c);
if (x > max) {
max = x;
}
}
return Integer.toString(max + 1);
}
@Override
BTreeLeaf firstLeaf() {
return tree.getPage(this, children[0]).firstLeaf();
}
@Override
void split(BTreeNode newParent, String newName, int pos, String siblingName) {
setParent(newParent, newName, true);
String[] k2 = Arrays.copyOfRange(keys, pos + 1, keys.length, String[].class);
String[] v2 = Arrays.copyOfRange(values, pos + 1, values.length, String[].class);
String[] c2 = Arrays.copyOfRange(children, pos + 1, children.length, String[].class);
BTreeNode n2 = new BTreeNode(tree, parent, siblingName, k2, v2, c2);
for (String c : c2) {
BTreePage cp = tree.getPageIfCached(this, c);
if (cp != null) {
cp.setParent(n2, c, false);
}
}
keys = Arrays.copyOfRange(keys, 0, pos, String[].class);
values = Arrays.copyOfRange(values, 0, pos, String[].class);
children = Arrays.copyOfRange(children, 0, pos + 1, String[].class);
n2.writeCreate();
for (String c : n2.children) {
tree.bufferMove(
PathUtils.concat(tree.getName(), getPath(), c),
PathUtils.concat(tree.getName(), getParentPath(), siblingName, c)
);
}
tree.moveCache(getPath());
writeData();
}
BTreeLeaf next(BTreePage child) {
int i = 0;
String childName = child.name;
for (; i < children.length; i++) {
if (children[i].equals(childName)) {
break;
}
}
if (i == children.length - 1) {
return parent == null ? null : parent.next(this);
}
return tree.getPage(this, children[i + 1]).firstLeaf();
}
BTreePage getChild(int pos) {
return tree.getPage(this, children[pos]);
}
void writeData() {
tree.modified(this);
tree.bufferSetArray(getPath(), "keys", keys);
tree.bufferSetArray(getPath(), "values", values);
tree.bufferSetArray(getPath(), "children", children);
}
@Override
void writeCreate() {
tree.modified(this);
JsopBuilder jsop = new JsopBuilder();
jsop.tag('+').key(PathUtils.concat(tree.getName(), getPath())).object();
jsop.key("keys").array();
for (String k : keys) {
jsop.value(k);
}
jsop.endArray();
jsop.key("values").array();
for (String v : values) {
jsop.value(v);
}
jsop.endArray();
// could just use child node list, but then
// new children need to be ordered at the right position,
// and we would need a way to distinguish empty lists
// from a leaf
jsop.key("children").array();
for (String d : children) {
jsop.value(d);
}
jsop.endArray();
jsop.endObject();
jsop.newline();
tree.buffer(jsop.toString());
}
void delete(int pos) {
tree.modified(this);
if (size() > 0) {
// empty parent
keys = ArrayUtils.arrayRemove(keys, Math.max(0, pos - 1));
values = ArrayUtils.arrayRemove(values, Math.max(0, pos - 1));
}
children = ArrayUtils.arrayRemove(children, pos);
}
void insert(int pos, String key, String value, String child) {
tree.modified(this);
keys = ArrayUtils.arrayInsert(keys, pos, key);
values = ArrayUtils.arrayInsert(values, pos, value);
children = ArrayUtils.arrayInsert(children, pos + 1, child);
}
boolean isEmpty() {
return children.length == 0;
}
}