package org.basex.data;
import static org.basex.core.Text.*;
import java.io.*;
import java.util.*;
import org.basex.io.in.DataInput;
import org.basex.io.out.DataOutput;
import org.basex.util.*;
import org.basex.util.hash.*;
import org.basex.util.list.*;
/**
* This class stores a single namespace node.
*
* @author BaseX Team 2005-17, BSD License
* @author Christian Gruen
*/
final class NSNode {
/** Child nodes. */
private NSNode[] nodes;
/** Number of children. */
private int size;
/** Parent node. */
private NSNode parent;
/** Dense array with ids of prefix/namespace uri pairs. */
private int[] values;
/** Pre value. */
private int pre;
/**
* Default constructor.
* @param pre pre value
*/
NSNode(final int pre) {
this.pre = pre;
values = new int[0];
nodes = new NSNode[0];
}
/**
* Constructor, specifying an input stream.
* @param in input stream
* @param parent parent reference
* @throws IOException I/O exception
*/
NSNode(final DataInput in, final NSNode parent) throws IOException {
this.parent = parent;
pre = in.readNum();
values = in.readNums();
size = in.readNum();
nodes = new NSNode[size];
for(int n = 0; n < size; ++n) nodes[n] = new NSNode(in, this);
}
/**
* Writes a single node to disk.
* @param out output stream
* @throws IOException I/O exception
*/
void write(final DataOutput out) throws IOException {
out.writeNum(pre);
out.writeNums(values);
out.writeNum(size);
for(int c = 0; c < size; ++c) nodes[c].write(out);
}
/**
* Returns the specified child.
* @param i index
* @return child
*/
NSNode child(final int i) {
return nodes[i];
}
/**
* Returns the number of children.
* @return number of children
*/
int children() {
return size;
}
/**
* Returns the pre value.
* @return pre value
*/
int pre() {
return pre;
}
/**
* Returns the parent node.
* @return parent node
*/
NSNode parent() {
return parent;
}
/**
* Returns the ids of prefix/namespace uri pairs.
* @return prefix/namespace uri pairs
*/
int[] values() {
return values;
}
// Requesting Namespaces ========================================================================
/**
* Finds the namespace node that is located closest to the specified pre value.
* @param p pre value
* @param data data reference
* @return node
*/
NSNode find(final int p, final Data data) {
// return this node if the pre values of all children are greater than the searched value
final int s = find(p);
if(s == -1) return this;
final NSNode ch = nodes[s];
final int cp = ch.pre;
// return exact hit
if(cp == p) return ch;
// found node is preceding sibling
if(cp + data.size(cp, Data.ELEM) <= p) return this;
// continue recursive search
return nodes[s].find(p, data);
}
/**
* Locates a child node with the specified pre value.
* <ul>
* <li> If the value is found, the position of the child node is returned.</li>
* <li> Otherwise, the position of the last child with a smaller pre value is returned.</li>
* <li> -1 is returned if all children have greater pre values.</li>
* </ul>
* @param p pre value
* @return position of the child node
*/
int find(final int p) {
int l = 0, h = size - 1;
while(l <= h) { // binary search
final int m = l + h >>> 1, v = nodes[m].pre;
if(v == p) return m;
if(v < p) l = m + 1;
else h = m - 1;
}
return l - 1;
}
/**
* Returns the id of the namespace uri for the specified prefix.
* @param prefix prefix reference
* @return if of the namespace uri, or {@code 0} if none is found
*/
int uri(final int prefix) {
final int[] vls = values;
final int vl = vls.length;
for(int v = 0; v < vl; v += 2) if(vls[v] == prefix) return vls[v + 1];
return 0;
}
// Updating Namespaces ==========================================================================
/**
* Deletes nodes in the specified range (p .. p + sz - 1) and updates the following pre values.
* @param p pre value
* @param s number of nodes to be deleted, or actually the size of the pre
* value which is to be deleted
*/
void delete(final int p, final int s) {
// find the node to deleted
int d = find(p);
// if the node is not directly contained as a child, either start at array index 0 or
// proceed with the next node in the child array to search for descendants of pre
if(d == -1 || nodes[d].pre != p) ++d;
// first pre value which is not deleted
final int upper = p + s;
// number of nodes to be deleted
int num = 0;
// determine number of nodes to be deleted
for(int i = d; i < size && nodes[i].pre < upper; ++i, ++num);
// new size of child array
size -= num;
// if all nodes are deleted, just create an empty array
if(size == 0) nodes = new NSNode[0];
// otherwise remove nodes from the child array
else if(num > 0) System.arraycopy(nodes, d + num, nodes, d, size - d);
}
/**
* Adds the specified node into the child array, which is sorted by pre values.
* @param node child node
*/
void add(final NSNode node) {
if(size == nodes.length)
nodes = Array.copy(nodes, new NSNode[Array.newSize(size)]);
// find inserting position
int s = find(node.pre);
if(s < 0 || node.pre != nodes[s].pre) s++;
System.arraycopy(nodes, s, nodes, s + 1, size++ - s);
nodes[s] = node;
node.parent = this;
}
/**
* Adds the specified prefix and URI reference.
* @param prefix prefix reference
* @param uri uri reference
*/
void add(final int prefix, final int uri) {
final int s = values.length;
values = Arrays.copyOf(values, s + 2);
values[s] = prefix;
values[s + 1] = uri;
}
/**
* Recursively deletes the specified namespace URI reference.
* @param uri namespace URI reference
*/
void delete(final int uri) {
for(int c = 0; c < size; ++c) nodes[c].delete(uri);
final int vl = values.length;
for(int v = 0; v < vl; v += 2) {
if(values[v + 1] != uri) continue;
final int[] vals = new int[vl - 2];
System.arraycopy(values, 0, vals, 0, v);
System.arraycopy(values, v + 2, vals, v, vl - v - 2);
values = vals;
break;
}
}
/**
* Recursive shifting of pre values after delete operations.
* @param start update location
* @param diff value to subtract from pre value
*/
void decrementPre(final int start, final int diff) {
if(pre >= start + diff) pre -= diff;
for(int c = 0; c < size; c++) nodes[c].decrementPre(start, diff);
}
/**
* Increments the pre value by the specified size.
* @param diff value to add to pre value
*/
void incrementPre(final int diff) {
pre += diff;
}
// Printing Namespaces ==========================================================================
/**
* Prints the node structure for debugging purposes.
* @param tb token builder
* @param level level
* @param ns namespace reference
* @param start start pre value
* @param end end pre value
*/
private void print(final TokenBuilder tb, final int level, final Namespaces ns, final int start,
final int end) {
if(pre >= start && pre <= end) {
tb.add(NL);
for(int i = 0; i < level; ++i) tb.add(" ");
tb.add(toString() + ' ');
final int[] vls = values;
final int vl = vls.length;
for(int i = 0; i < vl; i += 2) {
if(i != 0) tb.add(' ');
tb.add("xmlns");
final byte[] p = ns.prefix(vls[i]);
if(p.length != 0) tb.add(':');
tb.add(p).add("=\"").add(ns.uri(vls[i + 1])).add('"');
}
}
for(int c = 0; c < size; ++c) nodes[c].print(tb, level + 1, ns, start, end);
}
/**
* Adds the namespace structure of a node to the specified table.
* @param table table
* @param start first pre value
* @param end last pre value
* @param ns namespace reference
*/
void table(final Table table, final int start, final int end, final Namespaces ns) {
final int vl = values.length;
for(int i = 0; i < vl; i += 2) {
if(pre < start || pre > end) continue;
final TokenList tl = new TokenList();
tl.add(values[i + 1]);
tl.add(pre);
tl.add(pre - parent.pre);
tl.add(ns.prefix(values[i]));
tl.add(ns.uri(values[i + 1]));
table.contents.add(tl);
}
for(int i = 0; i < size; i++) nodes[i].table(table, start, end, ns);
}
/**
* Adds namespace information for the specified node to a map.
* @param map namespace map
* @param ns namespace reference
*/
void info(final TokenObjMap<TokenList> map, final Namespaces ns) {
final int vl = values.length;
for(int v = 0; v < vl; v += 2) {
final byte[] pref = ns.prefix(values[v]), uri = ns.uri(values[v + 1]);
TokenList prfs = map.get(uri);
if(prfs == null) {
prfs = new TokenList(1);
map.put(uri, prfs);
}
if(!prfs.contains(pref)) prfs.add(pref);
}
for(int c = 0; c < size; ++c) nodes[c].info(map, ns);
}
/**
* Prints the node structure.
* @param ns namespace reference
* @param start start pre value
* @param end end pre value
* @return string
*/
String toString(final Namespaces ns, final int start, final int end) {
final TokenBuilder tb = new TokenBuilder();
print(tb, 0, ns, start, end);
return tb.toString();
}
@Override
public String toString() {
return "Pre[" + pre + ']';
}
}