package org.basex.data;
import java.io.IOException;
import java.util.Arrays;
import org.basex.core.Context;
import org.basex.core.Prop;
import org.basex.io.out.ArrayOutput;
import org.basex.io.serial.Serializer;
import org.basex.util.Util;
import org.basex.util.list.IntList;
/**
* This class stores node references of a database in an ascending order.
* Instances of this class are used in the {@link Context} class to
* reference the currently used, marked, and copied nodes.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class Nodes implements Result {
/** Full-text position data (for visualization). */
public final FTPosData ftpos;
/** Root flag (nodes represent all document nodes of the database). */
public boolean root;
/** Root node. */
public Data data;
/** Pre values container. */
public int[] list;
/** Sorted pre values. */
public int[] sorted;
/**
* Constructor, specifying a database instance.
* @param d data reference
*/
public Nodes(final Data d) {
this(new int[0], d);
}
/**
* Constructor, specifying a single node and a database instance.
* @param n single node
* @param d data reference
*/
public Nodes(final int n, final Data d) {
this(new int[] { n }, d);
}
/**
* Constructor, specifying a node set and a database instance.
* @param n node set
* @param d data reference
*/
public Nodes(final int[] n, final Data d) {
this(n, d, Prop.gui ? new FTPosData() : null);
}
/**
* Constructor, specifying a node set, a database instance, and full-text
* positions.
* @param n node set
* @param d data reference
* @param ft ft position data
*/
public Nodes(final int[] n, final Data d, final FTPosData ft) {
data = d;
ftpos = ft;
set(n);
if(d == null) Util.notexpected("No data available");
}
/**
* Constructor, which should only used by test classes.
* No database reference is specified.
* @param n node set
*/
public Nodes(final int[] n) {
list = n;
ftpos = null;
}
@Override
public long size() {
return list.length;
}
@Override
public boolean sameAs(final Result v) {
final int s = list.length;
if(!(v instanceof Nodes) || v.size() != s) return false;
final Nodes n = (Nodes) v;
if(data != n.data) return false;
for(int c = 0; c < s; ++c) if(n.list[c] != list[c]) return false;
return ftpos == null || ftpos.sameAs(n.ftpos);
}
/**
* Checks if the node set contains all root nodes of the data instance,
* and sets the {@link #root} flag.
* @return self reference
*/
public Nodes checkRoot() {
final IntList docs = data.resources.docs();
if(list.length != docs.size()) {
root = false;
} else {
int c = -1;
while(++c < list.length && list[c] == docs.get(c));
root = c == list.length;
}
return this;
}
/**
* Checks if the specified node is contained in the array.
* @param p pre value
* @return true if the node was found
*/
public boolean contains(final int p) {
return find(p) >= 0;
}
/**
* Returns the position of the specified node or the negative value - 1 of
* the position where it should have been found.
* @param p pre value
* @return true if the node was found
*/
public int find(final int p) {
sort();
return Arrays.binarySearch(sorted, p);
}
/**
* Adds or removes the specified pre node.
* @param p pre value
*/
public void toggle(final int p) {
final int[] n = new int[] { p };
set(contains(p) ? except(list, n) : union(list, n));
}
/**
* Merges the specified array with the existing pre nodes.
* @param p pre value
*/
public void union(final int[] p) {
set(union(list, p));
}
/**
* Merges two sorted integer arrays via union.
* Note that the input arrays must be sorted.
* @param ai first set
* @param bi second set
* @return resulting set
*/
private static int[] union(final int[] ai, final int[] bi) {
final int al = ai.length, bl = bi.length;
final IntList c = new IntList();
int a = 0, b = 0;
while(a != al && b != bl) {
final int d = ai[a] - bi[b];
c.add(d <= 0 ? ai[a++] : bi[b++]);
if(d == 0) ++b;
}
while(a != al) c.add(ai[a++]);
while(b != bl) c.add(bi[b++]);
return c.toArray();
}
/**
* Subtracts the second from the first array.
* Note that the input arrays must be sorted.
* @param ai first set
* @param bi second set
* @return resulting set
*/
private static int[] except(final int[] ai, final int[] bi) {
final int al = ai.length, bl = bi.length;
final IntList c = new IntList();
int a = 0, b = 0;
while(a != al && b != bl) {
final int d = ai[a] - bi[b];
if(d < 0) c.add(ai[a]);
else ++b;
if(d <= 0) ++a;
}
while(a != al) c.add(ai[a++]);
return c.toArray();
}
/**
* Sets the specified nodes.
* @param n values
*/
private void set(final int[] n) {
list = n;
sorted = null;
}
/**
* Creates a sorted node array. If the original array is already sorted,
* the same reference is used.
*/
private void sort() {
if(sorted != null) return;
int i = Integer.MIN_VALUE;
for(final int n : list) {
if(i > n) {
sorted = Arrays.copyOf(list, list.length);
Arrays.sort(sorted);
return;
}
i = n;
}
sorted = list;
}
@Override
public void serialize(final Serializer ser) throws IOException {
for(int c = 0; c < list.length && !ser.finished(); ++c) serialize(ser, c);
}
@Override
public void serialize(final Serializer ser, final int n) throws IOException {
ser.openResult();
ser.node(data, list[n], ftpos);
ser.closeResult();
}
@Override
public String toString() {
final ArrayOutput ao = new ArrayOutput();
try {
serialize(Serializer.get(ao));
} catch(final IOException ex) {
Util.notexpected(ex);
}
return ao.toString();
}
}