package org.basex.http.restxq; import static org.basex.http.restxq.RestXqText.*; import static org.basex.util.Token.*; import java.util.*; import java.util.regex.*; import org.basex.http.*; import org.basex.query.func.inspect.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.query.var.*; import org.basex.util.hash.*; import org.basex.util.list.*; /** * This class returns a Web Application Description Language (WADL) file, * listing all available RESTXQ services. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ final class RestXqWadl { /** HTTP connection. */ private final HTTPConnection conn; /** * Constructor. * @param conn HTTP connection */ RestXqWadl(final HTTPConnection conn) { this.conn = conn; } /** * Returns a WADL description for all available URIs. * @param modules available modules * @return WADL description */ synchronized FElem create(final HashMap<String, RestXqModule> modules) { // create root nodes final FElem application = new FElem(WADL + "application", WADL_URI).declareNS(); final String base = conn.req.getRequestURL().toString(); final FElem resources = elem("resources", application).add("base", base); // create children final TreeMap<String, FElem> map = new TreeMap<>(); for(final RestXqModule mod : modules.values()) { for(final RestXqFunction func : mod.functions()) { if(func.path == null) continue; final TokenObjMap<TokenList> xqdoc = func.function.doc(); final String path = func.path.toString(); final String methods = func.methods.toString().replaceAll("[^A-Z ]", ""); // create resource final FElem resource = new FElem(WADL + "resource", WADL_URI).add("path", path); map.put(path + '?' + methods, resource); // add documentation for path variables final Matcher var = Pattern.compile("\\$[^}]*").matcher(path); while(var.find()) { addParam(var.group().substring(1), "template", resource, xqdoc, func); } // create method, add function documentation final FElem method = elem("method", resource).add("name", methods); final TokenList descs = xqdoc != null ? xqdoc.get(Inspect.DOC_DESCRIPTION) : null; if(descs != null) for(final byte[] desc : descs) addDoc(desc, method); // create request final FElem request = elem("request", method); for(final RestXqParam rxp : func.queryParams) addParam(rxp.name, "query", request, xqdoc, func); for(final RestXqParam rxp : func.formParams) addParam(rxp.name, "query", request, xqdoc, func); for(final RestXqParam rxp : func.headerParams) addParam(rxp.name, "header", request, xqdoc, func); // create response final FElem response = elem("response", method); final FElem representation = elem("representation", response); representation.add("mediaType", HTTPConnection.mediaType(func.output).toString()); } } // add resources in sorted order for(final FElem elem : map.values()) resources.add(elem); return application; } /** * Adds a parameter and its documentation to the specified element. * @param name name of parameter * @param style style * @param root root element * @param xqdoc documentation * @param func function */ private static void addParam(final String name, final String style, final FElem root, final TokenObjMap<TokenList> xqdoc, final RestXqFunction func) { final FElem param = elem("param", root); param.add("name", name).add("style", style); final QNm qn = new QNm(name); for(final Var var : func.function.args) { if(var.name.eq(qn) && var.type != null) { param.add("type", var.type.toString()); } } addDoc(Inspect.doc(xqdoc, token(name)), param); } /** * Creates an element. * @param name name of element * @param parent parent node * @return element node */ private static FElem elem(final String name, final FElem parent) { final FElem elem = new FElem(WADL + name, WADL_URI); if(parent != null) parent.add(elem); return elem; } /** * Adds a documentation element to the specified element. * @param xqdoc documentation (may be {@code null}) * @param parent parent node */ private static void addDoc(final byte[] xqdoc, final FElem parent) { if(xqdoc == null) return; final FElem doc = elem("doc", parent); doc.namespaces().add(EMPTY, token(XHTML_URL)); Inspect.add(xqdoc, doc); } }