package org.basex.query.func; import static org.basex.query.QueryText.*; import static org.basex.query.util.Err.*; import static org.basex.util.Token.*; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.item.Item; import org.basex.query.item.ANode; import org.basex.query.item.NodeType; import org.basex.query.item.QNm; import org.basex.query.item.AtomType; import org.basex.query.item.Str; import org.basex.query.item.Uri; import org.basex.query.iter.ItemCache; import org.basex.query.iter.Iter; import org.basex.query.util.Err; import org.basex.util.Atts; import org.basex.util.InputInfo; import org.basex.util.XMLToken; /** * QName functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class FNQName extends StandardFunc { /** * Constructor. * @param ii input info * @param f function definition * @param e arguments */ public FNQName(final InputInfo ii, final Function f, final Expr... e) { super(ii, f, e); } @Override public Iter iter(final QueryContext ctx) throws QueryException { switch(sig) { case IN_SCOPE_PREFIXES: return inscope(ctx); default: return super.iter(ctx); } } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { // functions have 1 or 2 arguments... final Item it = expr[0].item(ctx, input); final Item it2 = expr.length == 2 ? expr[1].item(ctx, input) : null; switch(sig) { case RESOLVE_QNAME: return resolveQName(ctx, it, it2); case QNAME: return qName(it, it2); case LOCAL_NAME_FROM_QNAME: return lnFromQName(ctx, it); case PREFIX_FROM_QNAME: return prefixFromQName(ctx, it); case NAMESPACE_URI_FOR_PREFIX: return nsUriForPrefix(it, it2); case RESOLVE_URI: return resolveURI(ctx, it, it2); default: return super.item(ctx, ii); } } /** * Returns the in-scope prefixes of the specified node. * @param ctx query context * @return prefix sequence * @throws QueryException query exception */ private Iter inscope(final QueryContext ctx) throws QueryException { final ANode node = (ANode) checkType(expr[0].item(ctx, input), NodeType.ELM); final Atts ns = node.nsScope().add(XML, XMLURI); final int as = ns.size(); final ItemCache ic = new ItemCache(as); for(int a = 0; a < as; ++a) { final byte[] key = ns.name(a); if(key.length + ns.string(a).length != 0) ic.add(Str.get(key)); } return ic; } /** * Resolves a QName. * @param it qname * @param it2 item * @return prefix sequence * @throws QueryException query exception */ private Item qName(final Item it, final Item it2) throws QueryException { final byte[] uri = checkEStr(it); final byte[] name = checkEStr(it2); final byte[] str = !contains(name, ':') && eq(uri, XMLURI) ? concat(XMLC, name) : name; if(!XMLToken.isQName(str)) Err.value(input, AtomType.QNM, Str.get(name)); final QNm nm = new QNm(str, uri); if(nm.hasPrefix() && uri.length == 0) Err.value(input, AtomType.URI, Str.get(nm.uri())); return nm; } /** * Returns the local name of a QName. * @param ctx query context * @param it qname * @return prefix sequence * @throws QueryException query exception */ private Item lnFromQName(final QueryContext ctx, final Item it) throws QueryException { if(it == null) return null; final QNm nm = (QNm) checkType(it, AtomType.QNM); return AtomType.NCN.cast(Str.get(nm.local()), ctx, input); } /** * Returns the local name of a QName. * @param ctx query context * @param it qname * @return prefix sequence * @throws QueryException query exception */ private Item prefixFromQName(final QueryContext ctx, final Item it) throws QueryException { if(it == null) return null; final QNm nm = (QNm) checkType(it, AtomType.QNM); return nm.hasPrefix() ? AtomType.NCN.cast(Str.get(nm.prefix()), ctx, input) : null; } /** * Returns a new QName. * @param ctx query context * @param it qname * @param it2 item * @return prefix sequence * @throws QueryException query exception */ private Item resolveQName(final QueryContext ctx, final Item it, final Item it2) throws QueryException { final ANode base = (ANode) checkType(it2, NodeType.ELM); if(it == null) return null; final byte[] name = checkEStr(it); if(!XMLToken.isQName(name)) Err.value(input, AtomType.QNM, it); final QNm nm = new QNm(name); final byte[] pref = nm.prefix(); final byte[] uri = base.uri(pref, ctx); if(uri == null) NSDECL.thrw(input, pref); nm.uri(uri); return nm; } /** * Returns the namespace URI for a prefix. * @param it qname * @param it2 item * @return prefix sequence * @throws QueryException query exception */ private Item nsUriForPrefix(final Item it, final Item it2) throws QueryException { final byte[] pref = checkEStr(it); final ANode an = (ANode) checkType(it2, NodeType.ELM); if(eq(pref, XML)) return Uri.uri(XMLURI); final Atts at = an.nsScope(); final int i = at != null ? at.get(pref) : -1; return i == -1 || at.string(i).length == 0 ? null : Uri.uri(at.string(i)); } /** * Resolves a URI. * @param ctx query context * @param it item * @param it2 second item * @return prefix sequence * @throws QueryException query exception */ private Item resolveURI(final QueryContext ctx, final Item it, final Item it2) throws QueryException { if(it == null) return null; final Uri rel = Uri.uri(checkEStr(it)); if(!rel.isValid()) URIINV.thrw(input, it); if(rel.isAbsolute()) return rel; final Uri base = it2 == null ? ctx.sc.baseURI() : Uri.uri(checkEStr(it2)); if(!base.isValid()) URIINV.thrw(input, base); if(!base.isAbsolute()) URIABS.thrw(input, base); return base.resolve(rel); } }