/** * Updates (renames) an element, attribute or pi name. * * @param pre pre value * @param kind node kind * @param name new tag, attribute or pi name * @param uri uri */ public final void update(final int pre, final int kind, final byte[] name, final byte[] uri) { meta.update(); if (kind == PI) { updateText(pre, trim(concat(name, SPACE, atom(pre))), kind); } else { // update/set namespace reference final int ouri = nspaces.uri(name, pre, this); final boolean ne = ouri == 0 && uri.length != 0; final int npre = kind == ATTR ? parent(pre, kind) : pre; final int nuri = ne ? nspaces.add(npre, npre, prefix(name), uri, this) : ouri != 0 && eq(nspaces.uri(ouri), uri) ? ouri : 0; // write namespace uri reference table.write1(pre, kind == ELEM ? 3 : 11, nuri); // write name reference table.write2( pre, 1, (nsFlag(pre) ? 1 << 15 : 0) | (kind == ELEM ? tagindex : atnindex).index(name, null, false)); // write namespace flag table.write2(npre, 1, (ne || nsFlag(npre) ? 1 << 15 : 0) | name(npre)); } }
/** * Returns the distance of the specified node. * * @param pre pre value * @param kind node kind * @return distance */ public int dist(final int pre, final int kind) { switch (kind) { case ELEM: return table.read4(pre, 4); case TEXT: case COMM: case PI: return table.read4(pre, 8); case ATTR: int d = table.read1(pre, 0) >> 3 & IO.MAXATTS; // skip additional attributes, if value is larger than maximum range if (d >= IO.MAXATTS) while (d < pre && kind(pre - d) == ATTR) d++; return d; default: return pre + 1; } }
/** * Deletes a node and its descendants. * * @param pre pre value of the node to delete */ public final void delete(final int pre) { meta.update(); // size of the subtree to delete int k = kind(pre); final int s = size(pre, k); resources.delete(pre, s); if (meta.updindex) { // delete child records from indexes indexDelete(pre, s); } /// explicitly delete text or attribute value if (k != DOC && k != ELEM) delete(pre, k != ATTR); // reduce size of ancestors int par = pre; // check if we are an attribute (different size counters) if (k == ATTR) { par = parent(par, ATTR); attSize(par, ELEM, attSize(par, ELEM) - 1); size(par, ELEM, size(par, ELEM) - 1); k = kind(par); } // reduce size of ancestors while (par > 0 && k != DOC) { par = parent(par, k); k = kind(par); size(par, k, size(par, k) - s); } // preserve empty root node if (kind(pre) == DOC) --meta.ndocs; if (meta.updindex) { // delete node and descendants from ID -> PRE map: idmap.delete(pre, id(pre), -s); } // delete node from table structure and reduce document size table.delete(pre, s); if (!cache) updateDist(pre, -s); // propagate PRE value shifts to namespaces nspaces.delete(pre, s, this); }
/** * Sets the disk offset of a text/attribute value. * * @param pre pre value * @param off offset */ final void textOff(final int pre, final long off) { table.write5(pre, 3, off); }
/** * Sets the size value. * * @param pre pre reference * @param kind node kind * @param value value to be stored */ public final void size(final int pre, final int kind, final int value) { if (kind == ELEM || kind == DOC) table.write4(pre, 8, value); }
/** * Inserts a data instance at the specified pre value. Note that the specified data instance must * differ from this instance. * * @param ipre value at which to insert new data * @param ipar parent pre value of node * @param clip data clip */ public final void insert(final int ipre, final int ipar, final DataClip clip) { meta.update(); // update value and document indexes if (meta.updindex) indexBegin(); resources.insert(ipre, clip); final int dsize = clip.size(); final int buf = Math.min(dsize, IO.BLOCKSIZE >> IO.NODEPOWER); // resize buffer to cache more entries buffer(buf); // find all namespaces in scope to avoid duplicate declarations final TokenMap nsScope = nspaces.scope(ipar, this); // loop through all entries final IntList preStack = new IntList(); final NSNode nsRoot = nspaces.current(); final HashSet<NSNode> newNodes = new HashSet<NSNode>(); final IntList flagPres = new IntList(); // indicates if database only contains a dummy node final Data data = clip.data; int c = 0; for (int dpre = clip.start; dpre < clip.end; ++dpre, ++c) { if (c != 0 && c % buf == 0) insert(ipre + c - buf); final int pre = ipre + c; final int dkind = data.kind(dpre); final int dpar = data.parent(dpre, dkind); // ipar < 0 if document nodes on top level are added final int dis = dpar >= 0 ? dpre - dpar : ipar >= 0 ? pre - ipar : 0; final int par = dis == 0 ? -1 : pre - dis; if (c == 0) nspaces.root(par, this); while (!preStack.isEmpty() && preStack.peek() > par) nspaces.close(preStack.pop()); switch (dkind) { case DOC: // add document nspaces.prepare(); final int s = data.size(dpre, dkind); doc(pre, s, data.text(dpre, true)); meta.ndocs++; preStack.push(pre); break; case ELEM: // add element nspaces.prepare(); boolean ne = false; if (data.nsFlag(dpre)) { final Atts at = data.ns(dpre); for (int a = 0; a < at.size(); ++a) { // see if prefix has been declared/ is part of current ns scope final byte[] old = nsScope.get(at.name(a)); if (old == null || !eq(old, at.value(a))) { // we have to keep track of all new NSNodes that are added // to the Namespace structure, as their pre values must not // be updated. I.e. if an NSNode N with pre value 3 existed // prior to inserting and two new nodes are inserted at // location pre == 3 we have to make sure N and only N gets // updated. newNodes.add(nspaces.add(at.name(a), at.value(a), pre)); ne = true; } } } byte[] nm = data.name(dpre, dkind); elem( dis, tagindex.index(nm, null, false), data.attSize(dpre, dkind), data.size(dpre, dkind), nspaces.uri(nm, true), ne); preStack.push(pre); break; case TEXT: case COMM: case PI: // add text text(pre, dis, data.text(dpre, true), dkind); break; case ATTR: // add attribute nm = data.name(dpre, dkind); // check if prefix already in nsScope or not final byte[] attPref = prefix(nm); // check if prefix of attribute has already been declared, otherwise // add declaration to parent node if (data.nsFlag(dpre) && nsScope.get(attPref) == null) { nspaces.add( par, preStack.isEmpty() ? -1 : preStack.peek(), attPref, data.nspaces.uri(data.uri(dpre, dkind)), this); // save pre value to set ns flag later for this node. can't be done // here as direct table access would interfere with the buffer flagPres.add(par); } attr( pre, dis, atnindex.index(nm, null, false), data.text(dpre, false), nspaces.uri(nm, false), false); break; } } // finalize and update namespace structure while (!preStack.isEmpty()) nspaces.close(preStack.pop()); nspaces.root(nsRoot); if (bp != 0) insert(ipre + c - 1 - (c - 1) % buf); // reset buffer to old size buffer(1); // set ns flags for (int f = 0; f < flagPres.size(); f++) { final int fl = flagPres.get(f); table.write2(fl, 1, name(fl) | 1 << 15); } // increase size of ancestors int p = ipar; while (p >= 0) { final int k = kind(p); size(p, k, size(p, k) + dsize); p = parent(p, k); } if (meta.updindex) { // add the entries to the ID -> PRE mapping: idmap.insert(ipre, id(ipre), dsize); indexEnd(); } if (!cache) updateDist(ipre + dsize, dsize); // propagate PRE value shifts to namespaces if (ipar != -1) nspaces.insert(ipre, dsize, newNodes); }
/** * Replaces parts of the database with the specified data instance. * * @param rpre pre value to be replaced * @param clip data clip */ public final void replace(final int rpre, final DataClip clip) { meta.update(); final int dsize = clip.size(); final Data data = clip.data; final int rkind = kind(rpre); final int rsize = size(rpre, rkind); final int rpar = parent(rpre, rkind); final int diff = dsize - rsize; buffer(dsize); resources.replace(rpre, rsize, clip); if (meta.updindex) { // update index indexDelete(rpre, rsize); indexBegin(); } for (int dpre = clip.start; dpre < clip.end; ++dpre) { final int dkind = data.kind(dpre); final int dpar = data.parent(dpre, dkind); final int pre = rpre + dpre - clip.start; final int dis = dpar >= 0 ? dpre - dpar : pre - rpar; switch (dkind) { case DOC: // add document doc(pre, data.size(dpre, dkind), data.text(dpre, true)); meta.ndocs++; break; case ELEM: // add element byte[] nm = data.name(dpre, dkind); elem( dis, tagindex.index(nm, null, false), data.attSize(dpre, dkind), data.size(dpre, dkind), nspaces.uri(nm, true), false); break; case TEXT: case COMM: case PI: // add text text(pre, dis, data.text(dpre, true), dkind); break; case ATTR: // add attribute nm = data.name(dpre, dkind); attr( pre, dis, atnindex.index(nm, null, false), data.text(dpre, false), nspaces.uri(nm, false), false); break; } } if (meta.updindex) { indexEnd(); // update ID -> PRE map: idmap.delete(rpre, id(rpre), -rsize); idmap.insert(rpre, meta.lastid - dsize + 1, dsize); } // update table: table.replace(rpre, buffer(), rsize); buffer(1); // no distance/size update if the two subtrees are of equal size if (diff == 0) return; // increase/decrease size of ancestors, adjust distances of siblings int p = rpar; while (p >= 0) { final int k = kind(p); size(p, k, size(p, k) + diff); p = parent(p, k); } if (!cache) updateDist(rpre + dsize, diff); // adjust attribute size of parent if attributes inserted. attribute size // of parent cannot be reduced via a replace expression. int dpre = clip.start; if (data.kind(dpre) == ATTR) { int d = 0; while (dpre < clip.end && data.kind(dpre++) == ATTR) d++; if (d > 1) attSize(rpar, kind(rpar), d + 1); } }
/** * Returns the node kind, which may be {@link #DOC}, {@link #ELEM}, {@link #TEXT}, {@link #ATTR}, * {@link #COMM} or {@link #PI}. * * @param pre pre value * @return node kind */ public final int kind(final int pre) { return table.read1(pre, 0) & 0x07; }
/** * Sets the namespace flag. Should be only called for element nodes. * * @param pre pre value * @param ne namespace flag */ public final void nsFlag(final int pre, final boolean ne) { table.write1(pre, 1, table.read1(pre, 1) & 0x7F | (ne ? 0x80 : 0)); }
/** * Returns the namespace flag of the addressed element. * * @param pre pre value * @return namespace flag */ public final boolean nsFlag(final int pre) { return (table.read1(pre, 1) & 0x80) != 0; }
/** * Returns a reference to the namespace of the addressed element or attribute. * * @param pre pre value * @param kind node kind * @return namespace URI */ public final int uri(final int pre, final int kind) { return kind == ELEM || kind == ATTR ? table.read1(pre, kind == ELEM ? 3 : 11) & 0xFF : 0; }
/** * Returns a reference to the tag or attribute name id. * * @param pre pre value * @return token reference */ public final int name(final int pre) { return table.read2(pre, 1) & 0x7FFF; }
/** * Returns a number of attributes. * * @param pre pre value * @param kind node kind * @return number of attributes */ public final int attSize(final int pre, final int kind) { int s = kind == ELEM ? table.read1(pre, 0) >> 3 & IO.MAXATTS : 1; // skip additional attributes, if value is larger than maximum range if (s >= IO.MAXATTS) while (s < meta.size - pre && kind(pre + s) == ATTR) s++; return s; }
/** * Returns a size value (number of descendant table entries). * * @param pre pre value * @param kind node kind * @return size value */ public final int size(final int pre, final int kind) { return kind == ELEM || kind == DOC ? table.read4(pre, 8) : 1; }
/** * Sets the distance. * * @param pre pre value * @param kind node kind * @param value value */ public void dist(final int pre, final int kind, final int value) { if (kind == ATTR) table.write1(pre, 0, value << 3 | ATTR); else if (kind != DOC) table.write4(pre, kind == ELEM ? 4 : 8, value); }
/** * Sets the attribute size. * * @param pre pre value * @param kind node kind * @param value value */ private void attSize(final int pre, final int kind, final int value) { if (kind == ELEM) table.write1(pre, 0, value << 3 | ELEM); }
/** * Returns the disk offset of a text (text, comment, pi) or attribute value. * * @param pre pre value * @return disk offset */ final long textOff(final int pre) { return table.read5(pre, 3); }
/** * Inserts the internal buffer to the storage without updating the table structure. * * @param pre insert position */ public final void insert(final int pre) { table.insert(pre, buffer()); }
/** * Returns a unique node id. * * @param pre pre value * @return node id */ public final int id(final int pre) { return table.read4(pre, 12); }