package org.basex.query.util.pkg; import static org.basex.query.util.Err.*; import static org.basex.query.util.pkg.PkgText.*; import static org.basex.util.Token.*; import java.io.IOException; import org.basex.io.IO; import org.basex.query.QueryException; import org.basex.query.QueryText; import org.basex.query.item.ANode; import org.basex.query.item.DBNode; import org.basex.query.item.NodeType; import org.basex.query.item.QNm; import org.basex.query.iter.AxisIter; import org.basex.query.util.pkg.Package.Component; import org.basex.query.util.pkg.Package.Dependency; import org.basex.util.InputInfo; import org.basex.util.Util; /** * Parses the package descriptors and performs schema checks. * * @author BaseX Team 2005-12, BSD License * @author Rositsa Shadura */ public final class PkgParser { /** Repository context. */ private final Repo repo; /** Input info. */ private final InputInfo input; /** * Constructor. * @param r repository context * @param ii input info */ public PkgParser(final Repo r, final InputInfo ii) { repo = r; input = ii; } /** * Parses package descriptor. * @param io XML input * @return package container * @throws QueryException query exception */ public Package parse(final IO io) throws QueryException { final Package pkg = new Package(); try { final DBNode doc = new DBNode(io, repo.context.prop); final ANode node = childElements(doc).next(); // checks root node if(!eqNS(PACKAGE, node.qname())) PKGDESCINV.thrw(input, Util.info(WHICHELEM, node.qname())); parseAttributes(node, pkg, PACKAGE); parseChildren(node, pkg); return pkg; } catch(final IOException ex) { throw PKGREADFAIL.thrw(input, io.name(), ex.getMessage()); } } /** * Parses the attributes of <package/> or <module/>. * @param node package node * @param p package container * @param root root node * @throws QueryException query exception */ private void parseAttributes(final ANode node, final Package p, final byte[] root) throws QueryException { final AxisIter atts = node.attributes(); for(ANode next; (next = atts.next()) != null;) { final byte[] name = next.name(); if(eq(NAME, name)) p.name = next.string(); else if(eq(ABBREV, name)) p.abbrev = next.string(); else if(eq(VERSION, name)) p.version = next.string(); else if(eq(SPEC, name)) p.spec = next.string(); else PKGDESCINV.thrw(input, Util.info(WHICHATTR, name)); } // check mandatory attributes if(p.name == null) PKGDESCINV.thrw(input, Util.info(MISSATTR, NAME, root)); if(p.version == null) PKGDESCINV.thrw(input, Util.info(MISSATTR, VERSION, root)); if(p.abbrev == null) PKGDESCINV.thrw(input, Util.info(MISSATTR, ABBREV, root)); if(p.spec == null) PKGDESCINV.thrw(input, Util.info(MISSATTR, SPEC, root)); } /** * Parses the children of <package/>. * @param node package node * @param p package container * @throws QueryException query exception */ private void parseChildren(final ANode node, final Package p) throws QueryException { final AxisIter ch = childElements(node); for(ANode next; (next = ch.next()) != null;) { final QNm name = next.qname(); if(eqNS(DEPEND, name)) p.dep.add(parseDependency(next)); else if(eqNS(XQUERY, name)) p.comps.add(parseComp(next)); } } /** * Parses <dependency/>. * @param node node <dependency/> to be parsed * @return dependency container * @throws QueryException query exception */ private Dependency parseDependency(final ANode node) throws QueryException { final AxisIter atts = node.attributes(); final Dependency d = new Dependency(); for(ANode next; (next = atts.next()) != null;) { final byte[] name = next.name(); if(eq(PKG, name)) d.pkg = next.string(); else if(eq(PROC, name)) d.processor = next.string(); else if(eq(VERS, name)) d.versions = next.string(); else if(eq(SEMVER, name)) d.semver = next.string(); else if(eq(SEMVERMIN, name)) d.semverMin = next.string(); else if(eq(SEMVERMAX, name)) d.semverMax = next.string(); else PKGDESCINV.thrw(input, Util.info(WHICHATTR, name)); } return d; } /** * Parses <xquery/>. * @param node xquery component * @return component container * @throws QueryException query exception */ private Component parseComp(final ANode node) throws QueryException { final AxisIter ch = childElements(node); final Component c = new Component(); for(ANode next; (next = ch.next()) != null;) { final QNm name = next.qname(); if(eqNS(NSPC, name)) c.uri = next.string(); else if(eqNS(FILE, name)) c.file = next.string(); else PKGDESCINV.thrw(input, Util.info(WHICHELEM, name)); } // check mandatory children if(c.uri == null) PKGDESCINV.thrw(input, Util.info(MISSCOMP, NSPC)); if(c.file == null) PKGDESCINV.thrw(input, Util.info(MISSCOMP, FILE)); return c; } /** * Returns an iterator on all child elements * (text and other nodes will be skipped). * @param node root node * @return child element iterator */ private static AxisIter childElements(final ANode node) { return new AxisIter() { /** Child iterator. */ final AxisIter ch = node.children(); @Override public ANode next() { while(true) { final ANode n = ch.next(); if(n == null || n.type == NodeType.ELM) return n; } } }; } /** * Checks if the specified name equals the qname and if it uses the packaging * namespace. * @param cmp input * @param name name to be compared * @return result of check */ private static boolean eqNS(final byte[] cmp, final QNm name) { return name.eq(new QNm(cmp, QueryText.PKGURI)); } }