package org.basex.query.func;
import static org.basex.query.util.Err.*;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.QueryText;
import org.basex.query.expr.Expr;
import org.basex.query.item.ANode;
import org.basex.query.item.Bln;
import org.basex.query.item.Item;
import org.basex.query.item.NodeType;
import org.basex.query.item.QNm;
import org.basex.query.item.Str;
import org.basex.query.item.Uri;
import org.basex.query.iter.AxisIter;
import org.basex.util.InputInfo;
import org.basex.util.Token;
import org.basex.util.TokenBuilder;
import org.basex.util.list.TokenList;
/**
* Node functions.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class FNNode extends StandardFunc {
/**
* Constructor.
* @param ii input info
* @param f function definition
* @param e arguments
*/
public FNNode(final InputInfo ii, final Function f, final Expr... e) {
super(ii, f, e);
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
// functions have 0 or 1 arguments...
final Item it = (expr.length != 0 ? expr[0] :
checkCtx(ctx)).item(ctx, input);
switch(sig) {
case NODE_NAME:
QNm qname = it != null ? checkNode(it).qname() : null;
return qname != null && qname.string().length != 0 ? qname : null;
case DOCUMENT_URI:
if(it == null) return null;
final ANode node = checkNode(it);
if(node.type != NodeType.DOC) return null;
final byte[] uri = node.baseURI();
return uri.length == 0 ? null : Uri.uri(uri);
case NILLED:
// always false, as no schema information is given
return it == null || checkNode(it).type != NodeType.ELM ? null :
Bln.FALSE;
case BASE_URI:
if(it == null) return null;
ANode n = checkNode(it);
if(n.type != NodeType.ELM && n.type != NodeType.DOC &&
n.parent() == null) return null;
Uri base = Uri.EMPTY;
while(!base.isAbsolute()) {
if(n == null) {
base = ctx.sc.baseURI().resolve(base);
break;
}
base = Uri.uri(n.baseURI()).resolve(base);
n = n.parent();
}
return base;
case NAME:
qname = it != null ? checkNode(it).qname() : null;
return qname != null ? Str.get(qname.string()) : Str.ZERO;
case LOCAL_NAME:
qname = it != null ? checkNode(it).qname() : null;
return qname != null ? Str.get(qname.local()) : Str.ZERO;
case NAMESPACE_URI:
qname = it != null ? checkNode(it).qname() : null;
return qname != null ? Uri.uri(qname.uri()) : Uri.EMPTY;
case ROOT:
if(it == null) return null;
n = checkNode(it);
while(n.parent() != null) n = n.parent();
return n;
case GENERATE_ID:
return it == null ? Str.ZERO : Str.get(new TokenBuilder(
QueryText.ID).addLong(checkNode(it).id).finish());
case HAS_CHILDREN:
return Bln.get(it != null && checkNode(it).hasChildren());
case PATH:
return it != null ? path(it) : null;
default:
return super.item(ctx, ii);
}
}
/**
* Performs the path function.
* @param it item to be resolved
* @return resulting iterator
* @throws QueryException query exception
*/
private Str path(final Item it) throws QueryException {
final TokenList tl = new TokenList();
ANode n = checkNode(it);
while(n.parent() != null) {
int i = 1;
final TokenBuilder tb = new TokenBuilder();
if(n.type == NodeType.ATT) {
tb.add('@');
final QNm qnm = n.qname();
final byte[] uri = qnm.uri();
if(uri.length != 0) tb.add('"').add(uri).add("\":");
tb.add(qnm.local());
} else if(n.type == NodeType.ELM) {
final QNm qnm = n.qname();
final AxisIter ai = n.precedingSibling();
for(ANode fs; (fs = ai.next()) != null;) if(fs.qname().eq(qnm)) i++;
tb.addExt("\"%\":%[%]", qnm.uri(), qnm.local(), i);
} else if(n.type == NodeType.COM || n.type == NodeType.TXT) {
final AxisIter ai = n.precedingSibling();
for(ANode fs; (fs = ai.next()) != null;) if(fs.type == n.type) i++;
tb.addExt(n.type() + "[%]", i);
} else if(n.type == NodeType.PI) {
final QNm qnm = n.qname();
final AxisIter ai = n.precedingSibling();
for(ANode fs; (fs = ai.next()) != null;) {
if(fs.type == n.type && fs.qname().eq(qnm)) i++;
}
tb.addExt("%(\"%\")[%]", n.type.string(), qnm.local(), i);
}
tl.add(tb.finish());
n = n.parent();
}
if(n.type != NodeType.DOC) IDDOC.thrw(input);
final TokenBuilder tb = new TokenBuilder();
for(int i = tl.size() - 1; i >= 0; --i) tb.add('/').add(tl.get(i));
return Str.get(tb.size() == 0 ? Token.SLASH : tb.finish());
}
@Override
public boolean uses(final Use u) {
return u == Use.X30 && (sig == Function.GENERATE_ID ||
sig == Function.PATH || sig == Function.HAS_CHILDREN ||
expr.length == 0 &&
(sig == Function.DOCUMENT_URI || sig == Function.NODE_NAME)) ||
u == Use.CTX && expr.length == 0 || super.uses(u);
}
}