package org.basex.data;
import static org.basex.core.Text.*;
import java.io.IOException;
import java.util.Arrays;
import org.basex.io.in.DataInput;
import org.basex.io.out.DataOutput;
import org.basex.util.TokenBuilder;
import org.basex.util.list.IntList;
/**
* This class stores a single namespace node.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
final class NSNode {
/** Children. */
NSNode[] ch;
/** Number of children. */
int size;
/** Parent node. */
NSNode par;
/** References to Prefix/URI pairs. */
int[] vals;
/** Pre value. */
int pre;
/**
* Default constructor.
* @param p pre value
*/
NSNode(final int p) {
vals = new int[0];
ch = new NSNode[0];
pre = p;
}
/**
* Constructor, specifying an input stream.
* @param in input stream
* @param p parent reference
* @throws IOException I/O exception
*/
NSNode(final DataInput in, final NSNode p) throws IOException {
par = p;
pre = in.readNum();
vals = in.readNums();
size = in.readNum();
ch = new NSNode[size];
for(int c = 0; c < size; ++c) ch[c] = 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(vals);
out.writeNum(size);
for(int c = 0; c < size; ++c) ch[c].write(out);
}
/**
* Sorts the specified node into the child array.
* @param n child node
* @return node
*/
NSNode add(final NSNode n) {
int s = size;
if(s == ch.length) {
final NSNode[] tmp = new NSNode[Math.max(s << 1, 1)];
System.arraycopy(ch, 0, tmp, 0, s);
ch = tmp;
}
while(--s >= 0 && n.pre - ch[s].pre <= 0);
System.arraycopy(ch, ++s, ch, s + 1, size++ - s);
ch[s] = n;
n.par = this;
return n;
}
/**
* Adds the specified prefix and URI reference.
* @param p prefix reference
* @param u uri reference
*/
void add(final int p, final int u) {
final int s = vals.length;
vals = Arrays.copyOf(vals, s + 2);
vals[s] = p;
vals[s + 1] = u;
}
/**
* Recursively deletes the specified namespace URI reference.
* @param uri namespace URI reference
*/
void delete(final int uri) {
for(final NSNode n : ch) n.delete(uri);
final IntList il = new IntList(vals.length);
for(int v = 0; v < vals.length; v += 2) {
if(vals[v + 1] != uri) {
il.add(vals[v]);
il.add(vals[v + 1]);
}
}
if(il.size() != vals.length) vals = il.toArray();
}
// Requesting Namespaces ====================================================
/**
* Finds the closest namespace node for the specified pre value.
* @param p pre value
* @return node
*/
NSNode find(final int p) {
final int s = fnd(p);
return s == -1 ? this : ch[s].pre == p ? ch[s] : ch[s].find(p);
}
/**
* Returns the namespace URI reference for the specified prefix.
* @param p prefix reference
* @return u uri reference or 0
*/
int uri(final int p) {
for(int v = 0; v < vals.length; v += 2) if(vals[v] == p) return vals[v + 1];
return 0;
}
/**
* Deletes nodes in the specified range (p .. p + sz - 1) and updates the
* following pre values
* @param p pre value
* @param sz 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 sz) {
// find the pre value which must be deleted
int s = fnd(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(s == -1 || ch[s].pre != p) ++s;
// first pre value which is not deleted
final int upper = p + sz;
// number of nodes to be deleted
int num = 0;
// determine number of nodes to be deleted
for(int i = s; i < size && ch[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) ch = new NSNode[0];
// otherwise remove nodes from the child array
else if(num > 0) System.arraycopy(ch, s + num, ch, s, size - s);
}
/**
* Finds a specific pre value in the child array utilizing binary search
* and returns its position.
* @param p pre value
* @return node
*/
int fnd(final int p) {
int l = 0, h = size - 1;
while(l <= h) { // binary search
final int m = l + h >>> 1;
final int v = ch[m].pre;
if(v == p) return m;
if(v < p) l = m + 1;
else h = m - 1;
}
return l - 1;
}
// Printing Namespaces ======================================================
/**
* Prints the node structure for debugging purposes.
* @param ns namespace reference
* @param s start pre value
* @param e end pre value
* @return string
*/
String print(final Namespaces ns, final int s, final int e) {
final TokenBuilder tb = new TokenBuilder();
print(tb, 0, ns, s, e);
return tb.toString();
}
/**
* Prints the node structure for debugging purposes.
* @param tb token builder
* @param l level
* @param ns namespace reference
* @param s start pre value
* @param e end pre value
*/
private void print(final TokenBuilder tb, final int l, final Namespaces ns,
final int s, final int e) {
if(pre >= s && pre <= e) {
tb.add(NL);
for(int i = 0; i < l; ++i) tb.add(" ");
tb.add(toString() + ' ');
for(int i = 0; i < vals.length; i += 2) {
tb.add("xmlns");
final byte[] p = ns.prefix(vals[i]);
if(p.length != 0) tb.add(':');
tb.add(p).add("=\"").add(ns.uri(vals[i + 1])).add("\" ");
}
}
for(int c = 0; c < size; ++c) ch[c].print(tb, l + 1, ns, s, e);
}
@Override
public String toString() {
return "Pre[" + pre + ']';
}
}