/** * Adds namespaces to the namespace stack. * * @param ctx query context * @return old stack position */ private int prepare(final QueryContext ctx) { final int s = ctx.sc.ns.size(); for (int n = 0; n < nspaces.size(); n++) { ctx.sc.ns.add(nspaces.name(n), nspaces.string(n)); } return s; }
/** * Inherits namespaces. * * @param node to be modified * @param nsp in-scope namespaces */ private static void inherit(final ANode node, final Atts nsp) { final Atts ns = node.namespaces(); for (int a = nsp.size() - 1; a >= 0; a--) { final byte[] pref = nsp.name(a); if (!ns.contains(pref)) ns.add(pref, nsp.string(a)); } }
/** * Adds the specified namespace to the namespace array. If the prefix is already used for another * URI, a new name is generated. * * @param pref prefix * @param uri uri * @param ns namespaces * @return resulting prefix */ private static byte[] addNS(final byte[] pref, final byte[] uri, final Atts ns) { final byte[] u = ns.string(pref); if (u == null) { // add undeclared namespace ns.add(pref, uri); } else if (!eq(u, uri)) { // prefixes with different URIs exist; new one must be replaced byte[] apref = null; // check if one of the existing prefixes can be adopted for (int c = 0; c < ns.size(); c++) { if (eq(ns.string(c), uri)) apref = ns.name(c); } // if negative, generate a new one that is not used yet if (apref == null) { int i = 1; do { apref = concat(pref, new byte[] {'_'}, token(i++)); } while (ns.contains(apref)); ns.add(apref, uri); } return apref; } return null; }
/** * Removes unused namespaces. * * @param node to be modified */ private static void noPreserve(final ANode node) { final Atts ns = node.namespaces(); final byte[] pref = node.qname().prefix(); for (int i = ns.size() - 1; i >= 0; i--) { boolean f = eq(ns.name(i), pref); final AxisIter atts = node.attributes(); for (ANode it; f && (it = atts.next()) != null; ) { f |= eq(it.qname().prefix(), pref); } if (!f) ns.delete(i); } }
/** * Returns a copy of the namespace hierarchy. * * @param sc static context (can be {@code null}) * @return namespaces */ public final Atts nsScope(final StaticContext sc) { final Atts ns = new Atts(); ANode node = this; do { final Atts nsp = node.namespaces(); if (nsp != null) { for (int a = nsp.size() - 1; a >= 0; a--) { final byte[] key = nsp.name(a); if (!ns.contains(key)) ns.add(key, nsp.value(a)); } } node = node.parent(); } while (node != null && node.type == NodeType.ELM); if (sc != null) sc.ns.inScope(ns); return ns; }
@Override protected void finishOpen() throws IOException { try { final AttributesImpl attrs = new AttributesImpl(); final int as = attributes.size(); for (int a = 0; a < as; a++) { final byte[] name = attributes.name(a); final String uri = string(namespaces.get(prefix(name))); final String lname = string(local(name)); final String rname = string(name); final String value = string(attributes.value(a)); attrs.addAttribute(uri, lname, rname, null, value); } final String uri = string(namespaces.get(elem.prefix())); final String lname = string(elem.local()); final String rname = string(elem.string()); contentHandler.startElement(uri, lname, rname, attrs); } catch (final SAXException ex) { throw new IOException(ex); } }
/** * 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); }
@Override public FElem item(final QueryContext ctx, final InputInfo ii) throws QueryException { final int s = prepare(ctx); try { // adds in-scope namespaces final Atts ns = new Atts(); for (int i = 0; i < nspaces.size(); ++i) { ns.add(nspaces.name(i), nspaces.string(i)); } // create and check QName final QNm nm = qname(ctx, ii); final byte[] cp = nm.prefix(), cu = nm.uri(); if (eq(cp, XML) ^ eq(cu, XMLURI)) CEXML.thrw(input, cu, cp); if (eq(cu, XMLNSURI)) CEINV.thrw(input, cu); if (eq(cp, XMLNS)) CEINV.thrw(input, cp); // analyze element namespace unless it is "xml" if (!eq(cp, XML)) { // request namespace for the specified uri final byte[] uri = ctx.sc.ns.uri(cp); // check if element has a namespace if (nm.hasURI()) { // add to statically known namespaces if (!comp && (uri == null || !eq(uri, cu))) ctx.sc.ns.add(cp, cu); // add to in-scope namespaces if (!ns.contains(cp)) ns.add(cp, cu); } else { // element has no namespace: assign default uri nm.uri(uri); } } // create child and attribute nodes final Constr constr = new Constr(ii, ctx).add(expr); if (constr.errAtt) NOATTALL.thrw(input); if (constr.errNS) NONSALL.thrw(input); if (constr.duplAtt != null) (comp ? CATTDUPL : ATTDUPL).thrw(input, constr.duplAtt); if (constr.duplNS != null) DUPLNSCONS.thrw(input, constr.duplNS); // create node final FElem node = new FElem(nm, constr.children, constr.atts, ns); // add namespaces from constructor final Atts cns = constr.nspaces; for (int a = 0; a < cns.size(); ++a) { addNS(cns.name(a), cns.string(a), ns); } // update parent references of attributes and add namespaces for (int a = 0; a < constr.atts.size(); ++a) { constr.atts.get(a).parent(node); final ANode att = constr.atts.get(a); final QNm qnm = att.qname(); // skip attributes without prefixes or URIs if (!qnm.hasPrefix() || !qnm.hasURI()) continue; // skip XML namespace final byte[] apref = qnm.prefix(); if (eq(apref, XML)) continue; final byte[] auri = qnm.uri(); final byte[] npref = addNS(apref, auri, ns); if (npref != null) { constr.atts.item[a] = new FAttr(new QNm(concat(npref, COLON, qnm.local()), auri), att.string()); } } // add inherited namespaces final Atts stack = ctx.sc.ns.stack(); for (int a = stack.size() - 1; a >= 0; a--) { final byte[] pref = stack.name(a); if (!ns.contains(pref)) ns.add(pref, stack.string(a)); } // update parent references of children for (int c = 0; c < constr.children.size(); ++c) { final ANode child = constr.children.get(c).parent(node); // add inherited and remove unused namespaces if (child.type == NodeType.ELM) { if (ctx.sc.nsInherit) inherit(child, ns); if (!ctx.sc.nsPreserve) noPreserve(child); child.optimize(); } } // return generated and optimized node return node.optimize(); } finally { ctx.sc.ns.size(s); } }
/** * Finds the prefix for the specified uri. * * @param uri namespace uri * @return prefix, or empty string */ public static byte[] prefix(final byte[] uri) { for (int s = NS.size() - 1; s >= 0; s--) { if (eq(NS.string(s), uri)) return NS.name(s); } return EMPTY; }
/** * Finds the specified namespace uri. * * @param pref prefix of the namespace * @return uri, or {@code null} */ public static byte[] uri(final byte[] pref) { for (int s = NS.size() - 1; s >= 0; s--) { if (eq(NS.name(s), pref)) return NS.string(s); } return null; }