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(); } }