package org.basex.query.func; import static org.basex.query.util.Err.*; import static org.basex.util.Token.*; import java.io.IOException; import java.nio.charset.Charset; import org.basex.data.Data; import org.basex.io.IO; import org.basex.io.IOContent; import org.basex.io.in.NewlineInput; import org.basex.io.out.ArrayOutput; import org.basex.io.serial.Serializer; import org.basex.io.serial.SerializerException; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.item.ANode; import org.basex.query.item.AtomType; import org.basex.query.item.Bln; import org.basex.query.item.DBNode; import org.basex.query.item.Item; import org.basex.query.item.NodeType; import org.basex.query.item.SeqType; import org.basex.query.item.Str; import org.basex.query.item.StrStream; import org.basex.query.item.Uri; import org.basex.query.item.Value; import org.basex.query.iter.Iter; import org.basex.query.up.primitives.Put; import org.basex.query.util.Err; import org.basex.query.util.Err.ErrType; import org.basex.util.InputInfo; import org.basex.util.list.ByteList; /** * Generating functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class FNGen extends StandardFunc { /** * Constructor. * @param ii input info * @param f function definition * @param e arguments */ public FNGen(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 DATA: return data(ctx); case COLLECTION: return collection(ctx).iter(); case URI_COLLECTION: return uriCollection(ctx); case UNPARSED_TEXT_LINES: return unparsedTextLines(ctx); default: return super.iter(ctx); } } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { switch(sig) { case DOC: return doc(ctx); case DOC_AVAILABLE: return docAvailable(ctx); case UNPARSED_TEXT: return unparsedText(ctx); case UNPARSED_TEXT_AVAILABLE: return unparsedTextAvailable(ctx); case PUT: return put(ctx); case PARSE_XML: return parseXml(ctx); case SERIALIZE: return serialize(ctx); default: return super.item(ctx, ii); } } @Override public Value value(final QueryContext ctx) throws QueryException { return sig == Function.COLLECTION ? collection(ctx) : super.value(ctx); } @Override public Expr cmp(final QueryContext ctx) { if(sig == Function.DATA && expr.length == 1) { final SeqType t = expr[0].type(); type = t.type.isNode() ? SeqType.get(AtomType.ATM, t.occ) : t; } return this; } /** * Performs the data function. * @param ctx query context * @return resulting iterator * @throws QueryException query exception */ private Iter data(final QueryContext ctx) throws QueryException { final Iter ir = ctx.iter(expr.length != 0 ? expr[0] : checkCtx(ctx)); return new Iter() { @Override public Item next() throws QueryException { final Item it = ir.next(); if(it == null) return null; if(it.type.isFunction()) FNATM.thrw(input, FNGen.this); return atom(it); } }; } /** * Performs the collection function. * @param ctx query context * @return result * @throws QueryException query exception */ private Value collection(final QueryContext ctx) throws QueryException { final Item it = expr.length != 0 ? expr[0].item(ctx, input) : null; return ctx.resource.collection( it != null ? string(checkEStr(it)) : null, input); } /** * Performs the uri-collection function. * @param ctx query context * @return result * @throws QueryException query exception */ private Iter uriCollection(final QueryContext ctx) throws QueryException { final Iter coll = collection(ctx).iter(); return new Iter() { @Override public Item next() throws QueryException { final Item it = coll.next(); // all items will be nodes return it == null ? null : Uri.uri(((ANode) it).baseURI()); } }; } /** * Performs the put function. * @param ctx query context * @return result * @throws QueryException query exception */ private Item put(final QueryContext ctx) throws QueryException { checkAdmin(ctx); final byte[] file = checkEStr(expr[1], ctx); final ANode nd = checkNode(checkNoEmpty(expr[0].item(ctx, input))); if(nd == null || nd.type != NodeType.DOC && nd.type != NodeType.ELM) UPFOTYPE.thrw(input, expr[0]); final Uri u = Uri.uri(file); if(u == Uri.EMPTY || !u.isValid()) UPFOURI.thrw(input, file); final DBNode target = ctx.updates.determineDataRef(nd, ctx); ctx.updates.add(new Put(input, target.pre, target.data, u, ctx), ctx); return null; } /** * Performs the doc function. * @param ctx query context * @return result * @throws QueryException query exception */ private ANode doc(final QueryContext ctx) throws QueryException { final Item it = expr[0].item(ctx, input); if(it == null) return null; final String in = string(checkEStr(it)); final Data d = ctx.resource.data(in, false, input); if(!d.single()) EXPSINGLE.thrw(input, in); return new DBNode(d, 0, Data.DOC); } /** * Performs the doc-available function. * @param ctx query context * @return result * @throws QueryException query exception */ private Bln docAvailable(final QueryContext ctx) throws QueryException { try { return Bln.get(doc(ctx) != null); } catch(final QueryException ex) { final Err err = ex.err(); if(err != null && err.type == ErrType.FODC && (err.num == 2 || err.num == 4)) return Bln.FALSE; throw ex; } } /** * Performs the unparsed-text function. * @param ctx query context * @return result * @throws QueryException query exception */ private StrStream unparsedText(final QueryContext ctx) throws QueryException { final IO io = checkIO(expr[0], ctx); final String enc = expr.length < 2 ? null : string(checkStr(expr[1], ctx)); if(enc != null && !Charset.isSupported(enc)) WHICHENC.thrw(input, enc); return new StrStream(io, enc, WRONGINPUT); } /** * Performs the unparsed-text-lines function. * @param ctx query context * @return result * @throws QueryException query exception */ Iter unparsedTextLines(final QueryContext ctx) throws QueryException { return textIter(unparsedText(ctx), input); } /** * Returns the specified text as lines. * @param si text input * @param ii input info * @return result * @throws QueryException query exception */ static Iter textIter(final StrStream si, final InputInfo ii) throws QueryException { final byte[] str = si.string(ii); return new Iter() { int p = -1; @Override public Item next() { final ByteList bl = new ByteList(); while(++p < str.length && str[p] != '\n') bl.add(str[p]); return p + 1 < str.length || bl.size() != 0 ? Str.get(bl.toArray()) : null; } }; } /** * Performs the unparsed-text-available function. * @param ctx query context * @return result * @throws QueryException query exception */ private Bln unparsedTextAvailable(final QueryContext ctx) throws QueryException { final IO io = checkIO(expr[0], ctx); final String enc = expr.length < 2 ? null : string(checkEStr(expr[1], ctx)); try { final NewlineInput nli = new NewlineInput(io, enc); try { while(nli.read() != -1); } finally { nli.close(); } return Bln.TRUE; } catch(final IOException ex) { return Bln.FALSE; } } /** * Performs the parse-xml function. * @param ctx query context * @return result * @throws QueryException query exception */ private ANode parseXml(final QueryContext ctx) throws QueryException { final byte[] cont = checkEStr(expr[0], ctx); Uri base = ctx.sc.baseURI(); if(expr.length == 2) { base = Uri.uri(checkEStr(expr[1], ctx)); if(!base.isValid()) BASEINV.thrw(input, base); } final IO io = new IOContent(cont, string(base.string())); try { return new DBNode(io, ctx.context.prop); } catch(final IOException ex) { throw SAXERR.thrw(input, ex); } } /** * Performs the serialize function. * @param ctx query context * @return result * @throws QueryException query exception */ private Str serialize(final QueryContext ctx) throws QueryException { final ArrayOutput ao = new ArrayOutput(); try { // run serialization final Serializer ser = Serializer.get(ao, serialPar(this, 1, ctx)); final Iter ir = expr[0].iter(ctx); for(Item it; (it = ir.next()) != null;) it.serialize(ser); ser.close(); } catch(final SerializerException ex) { throw ex.getCause(input); } catch(final IOException ex) { SERANY.thrw(input, ex); } return Str.get(delete(ao.toArray(), '\r')); } @Override public boolean uses(final Use u) { return u == Use.CNS && sig == Function.PARSE_XML || u == Use.UPD && sig == Function.PUT || u == Use.X30 && (sig == Function.DATA && expr.length == 0 || sig == Function.UNPARSED_TEXT || sig == Function.UNPARSED_TEXT_LINES || sig == Function.UNPARSED_TEXT_AVAILABLE || sig == Function.PARSE_XML || sig == Function.URI_COLLECTION || sig == Function.SERIALIZE) || u == Use.CTX && (sig == Function.DATA && expr.length == 0 || sig == Function.PUT) && expr.length == 0 || super.uses(u); } @Override public boolean iterable() { // collections will never yield duplicates return sig == Function.COLLECTION || super.iterable(); } }