/** * 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); }
/** * Returns the value for the specified pre value. * * @param pre pre value * @return item value */ byte[] getValue(final int pre) { final Data data = plotData.context.data; final int limit = pre + data.size(pre, Data.ELEM); for (int p = pre; p < limit; ++p) { final int kind = data.kind(p); if ((kind == Data.ELEM && tag || kind == Data.ATTR && !tag) && attrID == data.name(p)) return data.atom(p); } return EMPTY; }
/** * Creates an item iterator for the given XML fragment. * * @param xml fragment * @param frag fragment flag * @return iterator */ private ItemCache toIter(final String xml, final boolean frag) { final ItemCache it = new ItemCache(); try { String str = frag ? "<X>" + xml + "</X>" : xml; final Data d = CreateDB.xml(IO.get(str), context); for (int p = frag ? 2 : 0; p < d.meta.size; p += d.size(p, d.kind(p))) it.add(new DBNode(d, p)); } catch (final IOException ex) { return new ItemCache(new Item[] {Str.get(Long.toString(System.nanoTime()))}, 1); } return it; }
/** * Finds the closest namespace node for the specified pre value. * * @param pre pre value * @param data data reference * @return node */ NSNode find(final int pre, final Data data) { final int s = find(pre); // no match found: return current node if (s == -1) return this; final NSNode ch = children[s]; final int cp = ch.pr; // return exact hit if (cp == pre) return ch; // found node is preceding sibling if (cp + data.size(cp, Data.ELEM) <= pre) return this; // continue recursive search return children[s].find(pre, data); }
/** * Lists resources of the specified database. * * @return success flag * @throws IOException I/O exception */ private boolean listDB() throws IOException { final String db = args[0]; final String path = args[1] != null ? args[1] : ""; if (!Databases.validName(db)) return error(NAME_INVALID_X, db); final Table table = new Table(); table.description = RESOURCES; table.header.add(INPUT_PATH); table.header.add(TYPE); table.header.add(MimeTypes.CONTENT_TYPE); table.header.add(SIZE); try { // add xml documents final Data data = Open.open(db, context); final Resources res = data.resources; final IntList il = res.docs(path); final int ds = il.size(); for (int i = 0; i < ds; i++) { final int pre = il.get(i); final TokenList tl = new TokenList(3); final byte[] file = data.text(pre, true); tl.add(file); tl.add(DataText.M_XML); tl.add(MimeTypes.APP_XML); tl.add(data.size(pre, Data.DOC)); table.contents.add(tl); } // add binary resources for (final byte[] file : res.binaries(path)) { final String f = string(file); final TokenList tl = new TokenList(3); tl.add(file); tl.add(DataText.M_RAW); tl.add(MimeTypes.get(f)); tl.add(data.meta.binary(f).length()); table.contents.add(tl); } Close.close(data, context); } catch (final IOException ex) { return error(Util.message(ex)); } out.println(table.sort().finish()); return true; }
/** * Updates distances to restore parent-child relationships that have been invalidated by * structural updates. * * <p>Each structural update (insert/delete) leads to a shift of higher PRE values. This * invalidates parent-child relationships. Distances are only updated after all structural updates * have been carried out to make sure each node (that has to be updated) is only touched once. */ public void updateDistances() { accumulatePreValueShifts(); final IntSet alreadyUpdatedNodes = new IntSet(); for (final BasicUpdate update : updStructural) { int newPreOfAffectedNode = update.preOfAffectedNode + update.accumulatedShifts; /* Update distance for the affected node and all following siblings of nodes * on the ancestor-or-self axis. */ while (newPreOfAffectedNode < data.meta.size) { if (alreadyUpdatedNodes.contains(newPreOfAffectedNode)) break; data.dist( newPreOfAffectedNode, data.kind(newPreOfAffectedNode), calculateNewDistance(newPreOfAffectedNode)); alreadyUpdatedNodes.add(newPreOfAffectedNode); newPreOfAffectedNode += data.size(newPreOfAffectedNode, data.kind(newPreOfAffectedNode)); } } }
/** * Removes superfluous update operations. If a node T is deleted or replaced, all updates on the * descendant axis of T can be left out as they won't affect the database after all. * * <p>Superfluous updates can have a minimum PRE value of pre(T)+1 and a maximum PRE value of * pre(T)+size(T). * * <p>An update with location pre(T)+size(T) can only be removed if the update is an atomic insert * and the inserted node is then part of the subtree of T. */ public void optimize() { if (opt) return; check(); // traverse from lowest to highest PRE value int i = updStructural.size() - 1; while (i >= 0) { final BasicUpdate u = updStructural.get(i); // If this update can lead to superfluous updates ... if (u.destructive()) { // we determine the lowest and highest PRE values of a superfluous update final int pre = u.location; final int fol = pre + data.size(pre, data.kind(pre)); i--; // and have a look at the next candidate while (i >= 0) { final BasicUpdate desc = updStructural.get(i); final int descpre = desc.location; // if the candidate operates on the subtree of T and inserts a node ... if (descpre <= fol && (desc instanceof Insert || desc instanceof InsertAttr) && desc.parent() >= pre && desc.parent() < fol) { // it is removed. updStructural.remove(i--); // Other updates (not inserting a node) that operate on the subtree of T can // only have a PRE value that is smaller than the following PRE of T } else if (descpre < fol) { // these we delete. updStructural.remove(i--); // Else there's nothing to delete } else break; } } else i--; } opt = true; }
/** * Writes the entry for the specified pre value to the table. * * @param t table reference * @param data data reference * @param p node to be printed */ private static void table(final Table t, final Data data, final int p) { final int k = data.kind(p); final TokenList tl = new TokenList(); tl.add(p); tl.add(p - data.parent(p, k)); tl.add(data.size(p, k)); tl.add(data.attSize(p, k)); final int u = data.uri(p, k); if (data.nsFlag(p)) tl.add("+" + u); else tl.add(u); tl.add(TABLEKINDS[k]); byte[] cont = null; if (k == Data.ELEM) { cont = data.name(p, k); } else if (k == Data.ATTR) { cont = new TokenBuilder(data.name(p, k)).add(ATT1).add(data.text(p, false)).add(ATT2).finish(); } else { cont = data.text(p, true); } tl.add(replace(chop(cont, 64), '\n', ' ')); t.contents.add(tl); }
/** * 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 pre value of the next child. * * @return next child reference */ int next() { final int p = pre; pre += data.size(pre, data.kind(pre)); return p; }
/** * Initializes the iterator. * * @param p root pre value */ private void init(final int p) { final int k = data.kind(p); size = p + data.size(p, k); pre = p + data.attSize(p, k); }
/** * Adds a replace atomic to the list. * * @param pre PRE value of the target node/update location * @param d insertion sequence data reference */ public void addReplace(final int pre, final Data d) { final int oldsize = data.size(pre, data.kind(pre)); final int newsize = d.meta.size; add(new Replace(pre, newsize - oldsize, pre + oldsize, d), true); }
/** * Adds a delete atomic to the list. * * @param pre PRE value of the target node/update location */ public void addDelete(final int pre) { final int k = data.kind(pre); final int s = data.size(pre, k); add(new Delete(pre, -s, pre + s), true); }