diff --git a/src/main/java/info/textgrid/services/aggregator/Help.java b/src/main/java/info/textgrid/services/aggregator/Help.java new file mode 100644 index 0000000000000000000000000000000000000000..0a751c3d712f4e3ac6706a0468c85c0468d02406 --- /dev/null +++ b/src/main/java/info/textgrid/services/aggregator/Help.java @@ -0,0 +1,59 @@ +package info.textgrid.services.aggregator; + +import info.textgrid.services.aggregator.util.StylesheetManager; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.StreamingOutput; +import javax.ws.rs.core.UriBuilderException; +import javax.ws.rs.core.UriInfo; +import javax.xml.transform.stream.StreamSource; + +import net.sf.saxon.s9api.QName; +import net.sf.saxon.s9api.SaxonApiException; +import net.sf.saxon.s9api.Serializer; +import net.sf.saxon.s9api.XdmAtomicValue; +import net.sf.saxon.s9api.XsltTransformer; + +import com.google.common.base.Optional; + +public class Help implements StreamingOutput { + + private final StylesheetManager stylesheetManager; + private final URL wadlURL; + + public Help(StylesheetManager stylesheetManager, UriInfo uriInfo) { + this.stylesheetManager = stylesheetManager; + try { + wadlURL = uriInfo.getBaseUriBuilder().queryParam("_wadl").build().toURL(); + } catch (final MalformedURLException e) { + throw new WebApplicationException(e); + } catch (final IllegalArgumentException e) { + throw new WebApplicationException(e); + } catch (final UriBuilderException e) { + throw new WebApplicationException(e); + } + } + + @Override + public void write(OutputStream output) throws IOException, + WebApplicationException { + try { + final XsltTransformer transformer = stylesheetManager.getStylesheet(URI.create("/WEB-INF/stylesheets/wadl_documentation-2009-02.xsl"), Optional.<String>absent(), false, false).load(); + final Serializer serializer = stylesheetManager.xsltProcessor.newSerializer(output); + transformer.setDestination(serializer); + transformer.setSource(new StreamSource(wadlURL.openStream(), wadlURL.toString())); + transformer.setParameter(new QName("apptitle"), new XdmAtomicValue("TextGrid Aggregator Service")); + transformer.transform(); + } catch (final SaxonApiException e) { + throw new WebApplicationException(e); + } + + } + +} diff --git a/src/main/java/info/textgrid/services/aggregator/REST.java b/src/main/java/info/textgrid/services/aggregator/REST.java index 6f83df5312ea102afa68e30a5015b99216885e1f..69889c768eeff94ce81bcfc8b5f8a2f8c876a982 100644 --- a/src/main/java/info/textgrid/services/aggregator/REST.java +++ b/src/main/java/info/textgrid/services/aggregator/REST.java @@ -30,6 +30,7 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; +import javax.ws.rs.core.UriInfo; import net.sf.saxon.s9api.SaxonApiException; @@ -39,12 +40,14 @@ import com.google.common.base.Optional; +@Description(title = "TextGrid Aggregator Service", + value = "The Aggregator is a service to export and convert TextGrid documents. It is able to recursively process collections, editions, and other TextGrid aggregations.") public class REST { private final ITextGridRep repository; - private StylesheetManager stylesheetManager; + private StylesheetManager getStylesheetManager() { if (stylesheetManager == null) stylesheetManager = new StylesheetManager(servlet, repository); @@ -54,7 +57,6 @@ private StylesheetManager getStylesheetManager() { @Context private ServletContext servlet; - public REST(final ITextGridRep repository) { this.repository = repository; } @@ -63,18 +65,20 @@ public REST(final ITextGridRep repository) { @Path("/teicorpus/{uris}") @Produces("application/tei+xml") @Descriptions({ - @Description(target=DocTarget.METHOD, value="Creates a TEI corpus of all the TEI documents (recursively) aggregated by the given aggregation"), - @Description(target=DocTarget.RETURN, value="TEI corpus document") - }) - public Response getCorpus(@Description("TextGrid URIs of the root objects, separated by commas") @PathParam("uris") final String uriList, + @Description(target = DocTarget.METHOD, value = "Creates a TEI corpus of all the TEI documents (recursively) aggregated by the given aggregation"), + @Description(target = DocTarget.RETURN, value = "TEI corpus document") }) + public Response getCorpus( + @Description("TextGrid URIs of the root objects, separated by commas") @PathParam("uris") final String uriList, @Description("Whether to generate a Content-Disposition: attachment header") @QueryParam("attach") @DefaultValue("true") final boolean attach, @Description("If true, no intermediate TEI corpus documents will be generated for intermediate aggregations, hierarchical structure will be lost") @QueryParam("flat") @DefaultValue("false") final boolean flat, @Description("Title for the container if multiple root objects are given") @QueryParam("title") final String titleArgument, @Description("Session id for accessing restricted resources") @QueryParam("sid") final String sid, - @Context final Request request) - throws URISyntaxException, ObjectNotFoundFault, MetadataParseFault, IoFault, AuthFault, ProtocolNotImplementedFault, IOException, SaxonApiException { + @Context final Request request) throws URISyntaxException, + ObjectNotFoundFault, MetadataParseFault, IoFault, AuthFault, + ProtocolNotImplementedFault, IOException, SaxonApiException { - final TEICorpusExporter exporter = new TEICorpusExporter(repository, request, uriList); + final TEICorpusExporter exporter = new TEICorpusExporter(repository, + request, uriList); exporter.setFlat(flat); exporter.setTitle(titleArgument); exporter.sid(sid); @@ -82,22 +86,22 @@ public Response getCorpus(@Description("TextGrid URIs of the root objects, separ return exporter.createResponse().build(); } - @GET @Path(value = "/epub/{object}") @Produces(value = "application/epub+zip") @Description("Converts the given TEI object or the aggregation of TEI objects to an E-Book in EPUB format") public Response getEPUB( - @Description("The TextGrid URI(s) of the object(s) to convert, separated by commas. Should be either TEI objects or aggregations of TEI (and maybe other) objects") - @PathParam("object") final String uriList, + @Description("The TextGrid URI(s) of the object(s) to convert, separated by commas. Should be either TEI objects or aggregations of TEI (and maybe other) objects") @PathParam("object") final String uriList, @Description("URL of an alternative stylesheet to use. Must be compatible.") @QueryParam("stylesheet") final URI xsluri, @Description("Title if multiple root objects given") @QueryParam("title") final String titleParam, @Description("Session ID for accessing protected objects") @QueryParam("sid") final String sid, - @Context final Request request) - throws ObjectNotFoundFault, MetadataParseFault, IoFault, AuthFault, - ProtocolNotImplementedFault, IOException, SaxonApiException { + @Context final Request request) throws ObjectNotFoundFault, + MetadataParseFault, IoFault, AuthFault, + ProtocolNotImplementedFault, IOException, SaxonApiException { - final EPUBSerializer serializer = new EPUBSerializer(repository, getStylesheetManager(), uriList, Optional.fromNullable(sid), request); + final EPUBSerializer serializer = new EPUBSerializer(repository, + getStylesheetManager(), uriList, Optional.fromNullable(sid), + request); serializer.setStylesheet(xsluri); serializer.setTitle(titleParam); @@ -107,13 +111,17 @@ public Response getEPUB( @GET @Path(value = "/html/{object}") @Produces(value = "text/html") + @Descriptions({ + @Description(target = DocTarget.METHOD, value = "Generates HTML output. This is typically fast, and it is also used at textgridrep.de.", title = "HTML generator"), + @Description(target = DocTarget.RETURN, value = "Either an XHTML document (expect HTML5 elements), or a XHTML fragment containing only the body, if the embedded parameter is true.") + }) public Response getHTML( @Description("The TextGrid URIs of the TEI document(s) or aggregation(s) to transform, separated by commas") @PathParam("object") final String uriList, - @Description("If given, an alternative XSLT stylesheet to use") @QueryParam("stylesheet") final URI xsluri, - @Description("If true, check for an <?xsl-stylesheet?> processing instruction in the document to render") @QueryParam("pi") final boolean pi, + @Description("If given, an alternative XSLT stylesheet to use. Must be a textgrid URI.") @QueryParam("stylesheet") final URI xsluri, + @Description("If true, check for an <?xsl-stylesheet?> processing instruction in the document to render. Only textgrid: URIs will be resolved.") @QueryParam("pi") final boolean pi, @Description("If true and a stylesheet has been given, force reloading the stylesheet, do not cache") @QueryParam("refreshStylesheet") final boolean refreshStylesheet, @Description("Session ID to access protected resources") @QueryParam("sid") final String sid, - @Description("If true, pass the information the stylesheet that its result will be embedded into some website") @QueryParam("embedded") final boolean embedded, + @Description("If true, an HTML fragment consisting of a <div class='body'> element containing the contents of the HTML <body> will be returned, ready to be embedded in an existing HTML page") @QueryParam("embedded") final boolean embedded, @Description("URL of the CSS that should be referenced in the HTML that is created") @QueryParam("css") final URI css, @Description("The requested content type. E.g., text/html or text/xml") @QueryParam("mediatype") final String mediaType, @Description("An XML ID. If given, only this element will be transformed.") @QueryParam("id") final String id, @@ -122,51 +130,50 @@ public Response getHTML( ProtocolNotImplementedFault, WebApplicationException, IOException, SaxonApiException, ExecutionException { - - final HTMLWriter writer = new HTMLWriter(repository, getStylesheetManager(), uriList, xsluri, - refreshStylesheet, pi, embedded, css, sid, mediaType, id, request); + final HTMLWriter writer = new HTMLWriter(repository, + getStylesheetManager(), uriList, xsluri, refreshStylesheet, pi, + embedded, css, sid, mediaType, id, request); return writer.createResponse().build(); } @GET @Path(value = "/zip/{objects}") @Produces("application/zip") + @Descriptions({ + @Description(target=DocTarget.METHOD, value="Creates a ZIP containing the specified objects and everything that has been aggregated by them, " + + "optionally transformed and filtered. Links within supported XML documents will be rewritten to relative URLs if the target document " + + "is also packed into this ZIP. This method may take quite a while depending on the number of objects, and it will not start " + + "returning something until the metadata for everything that will be exported has been collected, so increase your timeouts ..."), + @Description(target=DocTarget.RETURN, value="The ZIP file returned will usually contain a content document plus a sidecar .meta file for each " + + "exported object. The .meta file contains the raw metadata according to the TextGrid metadata schema. For aggregations (editions, " + + "collections), we will typically create both a directory and an ORE content file containing the list of items. " + + "Additionally, a file called /.INDEX.imex will be included in the archive. This file contains the list of exported objects " + + "together with the local filename, the name of the local .meta file, the link rewriting method used, and the original TextGrid URI. " + + "You can use this file to re-import the whole set of files using the TextGridLab. See the Link Rewriter Library's documentation " + + "for an XML schema and description of the format. \n\nThe ZIP file may contain ZIP comments refering to warning or informational " + + "messages during export.") + }) public Response getZIP( - @Description("The TextGridURIs of the TEI documents or aggregations to zip, separated by commas (,)") - @PathParam("objects") final String uriList, - @Description("Session ID to access protected resources") - @QueryParam("sid") final String sid, - @Description("(optional) title for the exported data, currently only used for generating the filename. If none is given, the first title of the first object will be used.") - @QueryParam("title") final String title, - @QueryParam("filenames") - @DefaultValue("{parent|/}{author}-{title}*.{ext}") - @Description("Pattern for the generated filenames in the ZIP files.") final - String filenames, - @QueryParam("metanames") - @DefaultValue("{filename}.meta") - @Description("Pattern for the filenames for the metadata files in the ZIP files.") final - String metanames, - @QueryParam("dirnames") - @DefaultValue("{parent|/}{title}*") - @Description("Pattern for the directory names generated for aggregations etc. This pattern applied to the parent aggregation is available as {parent} in filenames and metanames.") final - String dirnames, - - @QueryParam("only") - @Description("Restrict export to objects with the given MIME types") - final List<String> only, - - @QueryParam("meta") - @Description("Export metadata and aggregation files") - @DefaultValue("true") - final boolean meta, - - @QueryParam("transform") - @Description("Transform XML documents") - final String transform, - - @Context final Request request) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault, ProtocolNotImplementedFault, IOException, SaxonApiException { - - final ZipResult zipResult = new ZipResult(repository, getStylesheetManager(), request, uriList, filenames, metanames, dirnames, only, meta, transform); + @Description("The TextGridURIs of the TEI documents or aggregations to zip, separated by commas (,)") @PathParam("objects") final String uriList, + @Description("Session ID to access protected resources") @QueryParam("sid") final String sid, + @Description("(optional) title for the exported data, currently only used for generating the filename. If none is given, the first title of the first object will be used.") @QueryParam("title") final String title, + @QueryParam("filenames") @DefaultValue("{parent|/}{author}-{title}*.{ext}") @Description("Pattern for the generated filenames in the ZIP files.") final String filenames, + @QueryParam("metanames") @DefaultValue("{filename}.meta") @Description("Pattern for the filenames for the metadata files in the ZIP files.") final String metanames, + @QueryParam("dirnames") @DefaultValue("{parent|/}{title}*") @Description("Pattern for the directory names generated for aggregations etc. This pattern applied to the parent aggregation is available as {parent} in filenames and metanames.") final String dirnames, + + @QueryParam("only") @Description("If at least one only parameter is given, restrict export to objects with the given MIME types") final List<String> only, + + @QueryParam("meta") @Description("Include metadata and aggregation files in the ZIP file.") @DefaultValue("true") final boolean meta, + + @QueryParam("transform") @Description("(EXPERIMENTAL) Transform each XML document before zipping. Values currently available are text, html, or the textgrid: URI of an XSLT stylesheet.") final String transform, + + @Context final Request request) throws MetadataParseFault, + ObjectNotFoundFault, IoFault, AuthFault, + ProtocolNotImplementedFault, IOException, SaxonApiException { + + final ZipResult zipResult = new ZipResult(repository, + getStylesheetManager(), request, uriList, filenames, metanames, + dirnames, only, meta, transform); if (title != null) zipResult.setTitle(title); if (sid != null) @@ -177,24 +184,34 @@ public Response getZIP( @GET @Path(value = "/pdf/{object}") @Produces("application/pdf") - public Response getPDF( - @PathParam("object") final URI uri, - @QueryParam("sid") final String sid, - @Context final Request request - ) throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault, ProtocolNotImplementedFault, IOException, SaxonApiException { - final PDF pdf = new PDF(repository, getStylesheetManager(), request, uri); + @Description("(BROKEN) the PDF export will currently not work on any installed instance of the service.") + public Response getPDF(@PathParam("object") final URI uri, + @QueryParam("sid") final String sid, @Context final Request request) + throws MetadataParseFault, ObjectNotFoundFault, IoFault, AuthFault, + ProtocolNotImplementedFault, IOException, SaxonApiException { + final PDF pdf = new PDF(repository, getStylesheetManager(), request, + uri); pdf.sid(sid); return pdf.createResponse().build(); } - @GET @Path(value = "/version") @Produces("text/html") + @Description("Produces an HTML page containing version and configuration information for the service instance.") public StreamingOutput getVersion() { final Version version = new Version(repository, getStylesheetManager()); return version.get(); } + @GET + @Path(value = "/help") + @Produces("text/html") + @Description("Returns an auto-generated help page summarizing all available arguments.") + public StreamingOutput getHelp(@Context UriInfo uriInfo) throws SaxonApiException, IOException { + return new Help(getStylesheetManager(), uriInfo); + } + + } diff --git a/src/main/webapp/WEB-INF/stylesheets/wadl_documentation-2009-02.xsl b/src/main/webapp/WEB-INF/stylesheets/wadl_documentation-2009-02.xsl new file mode 100644 index 0000000000000000000000000000000000000000..dca262864172ccddb412fa5876aeb5d576f4a9a7 --- /dev/null +++ b/src/main/webapp/WEB-INF/stylesheets/wadl_documentation-2009-02.xsl @@ -0,0 +1,824 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + wadl_documentation.xsl (2008-12-09) + + An XSLT stylesheet for generating HTML documentation from WADL, + by Mark Nottingham <mnot@yahoo-inc.com>. + + Copyright (c) 2006-2008 Yahoo! Inc. + + This work is licensed under the Creative Commons Attribution-ShareAlike 2.5 + License. To view a copy of this license, visit + http://creativecommons.org/licenses/by-sa/2.5/ + or send a letter to + Creative Commons + 543 Howard Street, 5th Floor + San Francisco, California, 94105, USA +--> +<!-- + * FIXME + - Doesn't inherit query/header params from resource/@type + - XML schema import, include, redefine don't import +--> +<!-- + * TODO + - forms + - link to or include non-schema variable type defs (as a separate list?) + - @href error handling +--> + +<xsl:stylesheet + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" + xmlns:wadl="http://wadl.dev.java.net/2009/02" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:ns="urn:namespace" + xmlns:fn="http://www.textgrid.de/ns/aggregator/fn" + xmlns="http://www.w3.org/1999/xhtml" + exclude-result-prefixes="xsl wadl xs html ns fn" +> + + <xsl:output + method="html" + encoding="UTF-8" + indent="yes" + doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" + doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" + /> + + <xsl:variable name="wadl-ns">http://wadl.dev.java.net/2009/02</xsl:variable> + <xsl:param name="apptitle">My Web Application</xsl:param> + <xsl:param name="prologHtml"/> + + <!-- expand @hrefs, @types into a full tree --> + + <xsl:variable name="resources"> + <xsl:apply-templates select="/wadl:application/wadl:resources" mode="expand"/> + </xsl:variable> + + <xsl:template match="wadl:resources" mode="expand"> + <xsl:variable name="base"> + <xsl:choose> + <xsl:when test="substring(@base, string-length(@base), 1) = '/'"> + <xsl:value-of select="substring(@base, 1, string-length(@base) - 1)"/> + </xsl:when> + <xsl:otherwise><xsl:value-of select="@base"/></xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:element name="resources" namespace="{$wadl-ns}"> + <xsl:for-each select="namespace::*"> + <xsl:variable name="prefix" select="name(.)"/> + <xsl:if test="$prefix"> + <xsl:attribute name="ns:{$prefix}"><xsl:value-of select="."/></xsl:attribute> + </xsl:if> + </xsl:for-each> + <xsl:apply-templates select="@*|node()" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:element> + </xsl:template> + + <xsl:template match="wadl:resource[@type]" mode="expand" priority="1"> + <xsl:param name="base"></xsl:param> + <xsl:variable name="uri" select="substring-before(@type, '#')"/> + <xsl:variable name="id" select="substring-after(@type, '#')"/> + <xsl:element name="resource" namespace="{$wadl-ns}"> + <xsl:attribute name="path"><xsl:value-of select="@path"/></xsl:attribute> + <xsl:choose> + <xsl:when test="$uri"> + <xsl:variable name="included" select="document($uri, /)"/> + <xsl:copy-of select="$included/descendant::wadl:resource_type[@id=$id]/@*"/> + <xsl:attribute name="id"><xsl:value-of select="@type"/>#<xsl:value-of select="@path"/></xsl:attribute> + <xsl:apply-templates select="$included/descendant::wadl:resource_type[@id=$id]/*" mode="expand"> + <xsl:with-param name="base" select="$uri"/> + </xsl:apply-templates> + </xsl:when> + <xsl:otherwise> + <xsl:copy-of select="//resource_type[@id=$id]/@*"/> + <xsl:attribute name="id"><xsl:value-of select="$base"/>#<xsl:value-of select="@type"/>#<xsl:value-of select="@path"/></xsl:attribute> + <xsl:apply-templates select="//wadl:resource_type[@id=$id]/*" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + <xsl:apply-templates select="node()" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:element> + </xsl:template> + + <xsl:template match="wadl:*[@href]" mode="expand"> + <xsl:param name="base"></xsl:param> + <xsl:variable name="uri" select="substring-before(@href, '#')"/> + <xsl:variable name="id" select="substring-after(@href, '#')"/> + <xsl:element name="{local-name()}" namespace="{$wadl-ns}"> + <xsl:copy-of select="@*"/> + <xsl:choose> + <xsl:when test="$uri"> + <xsl:attribute name="id"><xsl:value-of select="@href"/></xsl:attribute> + <xsl:variable name="included" select="document($uri, /)"/> + <xsl:apply-templates select="$included/descendant::wadl:*[@id=$id]/*" mode="expand"> + <xsl:with-param name="base" select="$uri"/> + </xsl:apply-templates> + </xsl:when> + <xsl:otherwise> + <xsl:attribute name="id"><xsl:value-of select="$base"/>#<xsl:value-of select="$id"/></xsl:attribute> + <!-- xsl:attribute name="id"><xsl:value-of select="generate-id()"/></xsl:attribute --> + <xsl:attribute name="element"><xsl:value-of select="//wadl:*[@id=$id]/@element"/></xsl:attribute> + <xsl:attribute name="mediaType"><xsl:value-of select="//wadl:*[@id=$id]/@mediaType"/></xsl:attribute> + <xsl:attribute name="status"><xsl:value-of select="//wadl:*[@id=$id]/@status"/></xsl:attribute> + <xsl:attribute name="name"><xsl:value-of select="//wadl:*[@id=$id]/@name"/></xsl:attribute> + <xsl:apply-templates select="//wadl:*[@id=$id]/*" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + </xsl:element> + </xsl:template> + + <xsl:template match="node()[@id]" mode="expand"> + <xsl:param name="base"></xsl:param> + <xsl:element name="{local-name()}" namespace="{$wadl-ns}"> + <xsl:copy-of select="@*"/> + <xsl:attribute name="id"><xsl:value-of select="$base"/>#<xsl:value-of select="@id"/></xsl:attribute> + <!-- xsl:attribute name="id"><xsl:value-of select="generate-id()"/></xsl:attribute --> + <xsl:apply-templates select="node()" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:element> + </xsl:template> + + <xsl:template match="@*|node()" mode="expand"> + <xsl:param name="base"></xsl:param> + <xsl:copy> + <xsl:apply-templates select="@*|node()" mode="expand"> + <xsl:with-param name="base" select="$base"/> + </xsl:apply-templates> + </xsl:copy> + </xsl:template> + +<!-- debug $resources + <xsl:template match="/"> + <xsl:copy-of select="$resources"/> + </xsl:template> +--> + + <!-- collect grammars (TODO: walk over $resources instead) --> + + <xsl:variable name="grammars"> + <xsl:copy-of select="/wadl:application/wadl:grammars/*[not(namespace-uri()=$wadl-ns)]"/> + <xsl:apply-templates select="/wadl:application/wadl:grammars/wadl:include[@href]" mode="include-grammar"/> + <xsl:apply-templates select="/wadl:application/wadl:resources/descendant::wadl:resource[@type]" mode="include-href"/> + <xsl:apply-templates select="$resources/descendant::wadl:*[@href]" mode="include-href"/> + </xsl:variable> + + + <xsl:template match="wadl:include[@href]" mode="include-grammar"> + <xsl:variable name="included" select="document(@href, /)/*"></xsl:variable> + <xsl:element name="wadl:include"> + <xsl:attribute name="href"><xsl:value-of select="@href"/></xsl:attribute> + <xsl:copy-of select="$included"/> <!-- FIXME: xml-schema includes, etc --> + </xsl:element> + </xsl:template> + + <xsl:template match="wadl:*[@href]" mode="include-href"> + <xsl:variable name="uri" select="substring-before(@href, '#')"/> + <xsl:if test="$uri"> + <xsl:variable name="included" select="document($uri, /)"/> + <xsl:copy-of select="$included/wadl:application/wadl:grammars/*[not(namespace-uri()=$wadl-ns)]"/> + <xsl:apply-templates select="$included/descendant::wadl:include[@href]" mode="include-grammar"/> + <xsl:apply-templates select="$included/wadl:application/wadl:resources/descendant::wadl:resource[@type]" mode="include-href"/> + <xsl:apply-templates select="$included/wadl:application/wadl:resources/descendant::wadl:*[@href]" mode="include-href"/> + </xsl:if> + </xsl:template> + + <xsl:template match="wadl:resource[@type]" mode="include-href"> + <xsl:variable name="uri" select="substring-before(@type, '#')"/> + <xsl:if test="$uri"> + <xsl:variable name="included" select="document($uri, /)"/> + <xsl:copy-of select="$included/wadl:application/wadl:grammars/*[not(namespace-uri()=$wadl-ns)]"/> + <xsl:apply-templates select="$included/descendant::wadl:include[@href]" mode="include-grammar"/> + <xsl:apply-templates select="$included/wadl:application/wadl:resources/descendant::wadl:resource[@type]" mode="include-href"/> + <xsl:apply-templates select="$included/wadl:application/wadl:resources/descendant::wadl:*[@href]" mode="include-href"/> + </xsl:if> + </xsl:template> + + <!-- main template --> + + <xsl:template match="/wadl:application"> + <html> + <head> + <title> + <xsl:choose> + <xsl:when test="wadl:doc[@title]"> + <xsl:value-of select="wadl:doc[@title][1]/@title"/> + </xsl:when> + <xsl:otherwise><xsl:value-of select="$apptitle"/></xsl:otherwise> + </xsl:choose> + </title> + <style type="text/css"> + body { + font-family: sans-serif; + font-size: 0.85em; + margin: 2em 8em; + } + .methods { + background-color: #ECF4FA; + padding: 1em; + } + h1 { + font-size: 2.5em; + color: #222; + font-weight: normal; + } + h2 { + border-bottom: 1px solid #888; + color: #222; + font-weight: normal; + margin-top: 1em; + margin-bottom: 0.5em; + font-size: 2em; + } + h3 { + color: #3685C4; + font-size: 1.75em; + margin-top: 1.25em; + margin-bottom: 0em; + } + h4 { + margin: 0em; + padding: 0em; + border-bottom: 2px solid white; + } + h6 { + font-size: 1.1em; + color: #99a; + margin: 0.5em 0em 0.25em 0em; + } + a:link { + color: #3685c4; + text-decoration: none; + border-bottom: 1px dashed #56a5e4; + } + a:visited { + color: #3685c4; + text-decoration: none; + border-bottom: 1px dashed #ddd; + } + a:hover { + border-bottom: 1px solid; + } + dd { + margin-left: 1em; + } + tt { + font-size: 1.2em; + } + table { + margin-bottom: 0.5em; + } + th { + text-align: left; + font-weight: normal; + color: black; + border-bottom: 1px solid black; + padding: 3px 6px; + } + td { + padding: 3px 6px; + vertical-align: top; + background-color: f6f6ff; + font-size: 0.85em; + } + td p { + margin: 0px; + } + ul { + padding-left: 1.75em; + } + p + ul, p + ol, p + dl { + margin-top: 0em; + } + .optional { + font-weight: normal; + opacity: 0.75; + } + </style> + </head> + <body> + <h1> + <xsl:choose> + <xsl:when test="wadl:doc[@title]"> + <xsl:value-of select="wadl:doc[@title][1]/@title"/> + </xsl:when> + <xsl:otherwise><xsl:value-of select="$apptitle"/></xsl:otherwise> + </xsl:choose> + </h1> + <xsl:value-of select="$prologHtml" disable-output-escaping="yes"/> + <xsl:apply-templates select="wadl:doc"/> + <ul> + <li><a href="#resources">Resources</a> + <xsl:apply-templates select="$resources" mode="toc"/> + </li> + <li><a href="#representations">Representations</a> + <ul> + <xsl:apply-templates select="$resources/descendant::wadl:representation" mode="toc"/> + </ul> + </li> + <xsl:if test="descendant::wadl:fault"> + <li><a href="#faults">Faults</a> + <ul> + <xsl:apply-templates select="$resources/descendant::wadl:fault" mode="toc"/> + </ul> + </li> + </xsl:if> + </ul> + <h2 id="resources">Resources</h2> + <xsl:apply-templates select="$resources" mode="list"/> + <h2 id="representations">Representations</h2> + <xsl:apply-templates select="$resources/descendant::wadl:representation" mode="list"/> + <xsl:if test="$resources/descendant::wadl:fault"><h2 id="faults">Faults</h2> + <xsl:apply-templates select="$resources/descendant::wadl:fault" mode="list"/> + </xsl:if> + </body> + </html> + </xsl:template> + + <!-- Table of Contents --> + + <xsl:template match="wadl:resources" mode="toc"> + <xsl:variable name="base"> + <xsl:choose> + <xsl:when test="substring(@base, string-length(@base), 1) = '/'"> + <xsl:value-of select="substring(@base, 1, string-length(@base) - 1)"/> + </xsl:when> + <xsl:otherwise><xsl:value-of select="@base"/></xsl:otherwise> + </xsl:choose> + </xsl:variable> + <ul> + <xsl:apply-templates select="wadl:resource" mode="toc"> + <xsl:with-param name="context"><xsl:value-of select="$base"/></xsl:with-param> + </xsl:apply-templates> + </ul> + </xsl:template> + + <xsl:function name="fn:path-join"> + <xsl:param name="left"/> + <xsl:param name="right"/> + <xsl:value-of select="$left"/> + <xsl:if test="$left != '' and not(ends-with(string-join($left, ''), '/') or starts-with(string-join($right, ''), '/'))"> + <xsl:text>/</xsl:text> + </xsl:if> + <xsl:value-of select="$right"/> + </xsl:function> + + <xsl:template match="wadl:resource" mode="toc"> + <xsl:param name="context"/> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <xsl:variable name="name" select="fn:path-join($context, @path)"/> + <li><a href="#{$id}"><xsl:value-of select="$name"/></a> + <xsl:if test="wadl:resource"> + <ul> + <xsl:apply-templates select="wadl:resource" mode="toc"> + <xsl:with-param name="context" select="$name"/> + </xsl:apply-templates> + </ul> + </xsl:if> + </li> + </xsl:template> + + <xsl:template match="wadl:representation|wadl:fault" mode="toc"> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <xsl:variable name="href" select="@id"/> + <xsl:choose> + <xsl:when test="preceding::wadl:*[@id=$href]"/> + <xsl:otherwise> + <li> + <a href="#{$id}"> + <xsl:call-template name="representation-name"/> + </a> + </li> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Listings --> + + <xsl:template match="wadl:resources" mode="list"> + <xsl:variable name="base"> + <xsl:choose> + <xsl:when test="substring(@base, string-length(@base), 1) = '/'"> + <xsl:value-of select="substring(@base, 1, string-length(@base) - 1)"/> + </xsl:when> + <xsl:otherwise><xsl:value-of select="@base"/></xsl:otherwise> + </xsl:choose> + </xsl:variable> + <xsl:apply-templates select="wadl:resource" mode="list"/> + + </xsl:template> + + <xsl:template match="wadl:resource" mode="list"> + <xsl:param name="context"/> + <xsl:variable name="href" select="@id"/> + <xsl:choose> + <xsl:when test="preceding::wadl:resource[@id=$href]"/> + <xsl:otherwise> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <xsl:variable name="name"> + <xsl:value-of select="fn:path-join($context, @path)"/> + <xsl:for-each select="wadl:param[@style='matrix']"> + <span class="optional">;<xsl:value-of select="@name"/>=...</span> + </xsl:for-each> + </xsl:variable> + + + <div class="resource"> + <h3 id="{$id}"> + <xsl:choose> + <xsl:when test="wadl:doc[@title]"><xsl:value-of select="wadl:doc[@title][1]/@title"/></xsl:when> + <xsl:otherwise> + <xsl:copy-of select="$name"/> + <xsl:for-each select="wadl:method[1]/wadl:request/wadl:param[@style='query']"> + <xsl:choose> + <xsl:when test="@required='true'"> + <xsl:choose> + <xsl:when test="preceding-sibling::wadl:param[@style='query']">&</xsl:when> + <xsl:otherwise>?</xsl:otherwise> + </xsl:choose> + <xsl:value-of select="@name"/> + </xsl:when> + <xsl:otherwise> + <span class="optional"> + <xsl:choose> + <xsl:when test="preceding-sibling::wadl:param[@style='query']">&</xsl:when> + <xsl:otherwise>?</xsl:otherwise> + </xsl:choose> + <xsl:value-of select="@name"/> + </span> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </xsl:otherwise> + </xsl:choose> + + </h3> + <xsl:apply-templates select="wadl:doc"/> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="prefix">resource-wide</xsl:with-param> + <xsl:with-param name="style">template</xsl:with-param> + </xsl:apply-templates> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="prefix">resource-wide</xsl:with-param> + <xsl:with-param name="style">matrix</xsl:with-param> + </xsl:apply-templates> + <h6>Methods</h6> + <div class="methods"> + <xsl:apply-templates select="wadl:method"/> + </div> + </div> + <xsl:apply-templates select="wadl:resource" mode="list"> + <xsl:with-param name="context" select="$name"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="wadl:method"> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <div class="method"> + <h4 id="{$id}"><xsl:value-of select="@name"/></h4> + <xsl:apply-templates select="wadl:doc"/> + <xsl:apply-templates select="wadl:request"/> + <xsl:apply-templates select="wadl:response"/> + </div> + </xsl:template> + + <xsl:template match="wadl:request"> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="prefix">request</xsl:with-param> + <xsl:with-param name="style">query</xsl:with-param> + </xsl:apply-templates> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="prefix">request</xsl:with-param> + <xsl:with-param name="style">header</xsl:with-param> + </xsl:apply-templates> + <xsl:if test="wadl:representation"> + <p><em>acceptable request representations:</em></p> + <ul> + <xsl:apply-templates select="wadl:representation"/> + </ul> + </xsl:if> + </xsl:template> + + <xsl:template match="wadl:response"> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="prefix">response</xsl:with-param> + <xsl:with-param name="style">header</xsl:with-param> + </xsl:apply-templates> + <xsl:if test="wadl:representation"> + <p><em>available response representations:</em></p> + <ul> + <xsl:apply-templates select="wadl:representation"/> + </ul> + </xsl:if> + <xsl:if test="wadl:fault"> + <p><em>potential faults:</em></p> + <ul> + <xsl:apply-templates select="wadl:fault"/> + </ul> + </xsl:if> + </xsl:template> + + <xsl:template match="wadl:representation|wadl:fault"> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <li> + <a href="#{$id}"> + <xsl:call-template name="representation-name"/> + </a> + </li> + </xsl:template> + + <xsl:template match="wadl:representation|wadl:fault" mode="list"> + <xsl:variable name="id"><xsl:call-template name="get-id"/></xsl:variable> + <xsl:variable name="href" select="@id"/> + <xsl:variable name="expanded-name"> + <xsl:call-template name="expand-qname"> + <xsl:with-param select="@element" name="qname"/> + </xsl:call-template> + </xsl:variable> + <xsl:choose> + <xsl:when test="preceding::wadl:*[@id=$href]"/> + <xsl:otherwise> + <h3 id="{$id}"> + <xsl:call-template name="representation-name"/> + </h3> + <xsl:apply-templates select="wadl:doc"/> + <xsl:if test="@element or wadl:param"> + <div class="representation"> + <xsl:if test="@element"> + <h6>XML Schema</h6> + <xsl:call-template name="get-element"> + <xsl:with-param name="context" select="."/> + <xsl:with-param name="qname" select="@element"/> + </xsl:call-template> + </xsl:if> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="style">plain</xsl:with-param> + </xsl:apply-templates> + <xsl:apply-templates select="." mode="param-group"> + <xsl:with-param name="style">header</xsl:with-param> + </xsl:apply-templates> + </div> + </xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="wadl:*" mode="param-group"> + <xsl:param name="style"/> + <xsl:param name="prefix"></xsl:param> + <xsl:if test="ancestor-or-self::wadl:*/wadl:param[@style=$style]"> + <h6><xsl:value-of select="$prefix"/><xsl:text> </xsl:text><xsl:value-of select="$style"/> parameters</h6> + <table> + <tr> + <th>parameter</th> + <th>value</th> + <th>description</th> + </tr> + <xsl:apply-templates select="ancestor-or-self::wadl:*/wadl:param[@style=$style]"/> + </table> + </xsl:if> + </xsl:template> + + <xsl:template match="wadl:param"> + <tr> + <td> + <p><strong><xsl:value-of select="@name"/></strong></p> + </td> + <td> + <p> + <em><xsl:call-template name="link-qname"><xsl:with-param name="qname" select="@type"/></xsl:call-template></em> + <xsl:if test="@required='true'"> <small> (required)</small></xsl:if> + <xsl:if test="@repeating='true'"> <small> (repeating)</small></xsl:if> + </p> + <xsl:choose> + <xsl:when test="wadl:option"> + <p><em>One of:</em></p> + <ul> + <xsl:apply-templates select="wadl:option"/> + </ul> + </xsl:when> + <xsl:otherwise> + <xsl:if test="@default"><p>Default: <tt><xsl:value-of select="@default"/></tt></p></xsl:if> + <xsl:if test="@fixed"><p>Fixed: <tt><xsl:value-of select="@fixed"/></tt></p></xsl:if> + </xsl:otherwise> + </xsl:choose> + </td> + <td> + <xsl:apply-templates select="wadl:doc"/> + <xsl:if test="wadl:option[wadl:doc]"> + <dl> + <xsl:apply-templates select="wadl:option" mode="option-doc"/> + </dl> + </xsl:if> + <xsl:if test="@path"> + <ul> + <li>XPath to value: <tt><xsl:value-of select="@path"/></tt></li> + <xsl:apply-templates select="wadl:link"/> + </ul> + </xsl:if> + </td> + </tr> + </xsl:template> + + <xsl:template match="wadl:link"> + <li> + Link: <a href="#{@resource_type}"><xsl:value-of select="@rel"/></a> + </li> + </xsl:template> + + <xsl:template match="wadl:option"> + <li> + <tt><xsl:value-of select="@value"/></tt> + <xsl:if test="ancestor::wadl:param[1]/@default=@value"> <small> (default)</small></xsl:if> + </li> + </xsl:template> + + <xsl:template match="wadl:option" mode="option-doc"> + <dt> + <tt><xsl:value-of select="@value"/></tt> + <xsl:if test="ancestor::wadl:param[1]/@default=@value"> <small> (default)</small></xsl:if> + </dt> + <dd> + <xsl:apply-templates select="wadl:doc"/> + </dd> + </xsl:template> + + <xsl:template match="wadl:doc"> + <xsl:param name="inline">0</xsl:param> + <!-- skip WADL elements --> + <xsl:choose> + <xsl:when test="node()[1]=text() and $inline=0"> + <p> + <xsl:apply-templates select="node()" mode="copy"/> + </p> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="node()" mode="copy"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- utilities --> + + <xsl:template name="get-id"> + <xsl:choose> + <xsl:when test="@id"><xsl:value-of select="@id"/></xsl:when> + <xsl:otherwise><xsl:value-of select="generate-id()"/></xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template name="get-namespace-uri"> + <xsl:param name="context" select="."/> + <xsl:param name="qname"/> + <xsl:variable name="prefix" select="substring-before($qname,':')"/> + <xsl:variable name="qname-ns-uri" select="$context/namespace::*[name()=$prefix]"/> + <!-- nasty hack to get around libxsl's refusal to copy all namespace nodes when pushing nodesets around --> + <xsl:choose> + <xsl:when test="$qname-ns-uri"> + <xsl:value-of select="$qname-ns-uri"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$resources/*[1]/attribute::*[namespace-uri()='urn:namespace' and local-name()=$prefix]"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template name="get-element"> + <xsl:param name="context" select="."/> + <xsl:param name="qname"/> + <xsl:variable name="ns-uri"> + <xsl:call-template name="get-namespace-uri"> + <xsl:with-param name="context" select="$context"/> + <xsl:with-param name="qname" select="$qname"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="localname" select="substring-after($qname, ':')"/> + <xsl:variable name="definition" select="$grammars/descendant::xs:element[@name=$localname][ancestor-or-self::*[@targetNamespace=$ns-uri]]"/> + <xsl:variable name='source' select="$definition/ancestor-or-self::wadl:include[1]/@href"/> + <p><em>Source: <a href="{$source}"><xsl:value-of select="$source"/></a></em></p> + <pre><xsl:apply-templates select="$definition" mode="encode"/></pre> + </xsl:template> + + <xsl:template name="link-qname"> + <xsl:param name="context" select="."/> + <xsl:param name="qname"/> + <xsl:variable name="ns-uri"> + <xsl:call-template name="get-namespace-uri"> + <xsl:with-param name="context" select="$context"/> + <xsl:with-param name="qname" select="$qname"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="localname" select="substring-after($qname, ':')"/> + <xsl:choose> + <xsl:when test="$ns-uri='http://www.w3.org/2001/XMLSchema'"> + <a href="http://www.w3.org/TR/xmlschema-2/#{$localname}"><xsl:value-of select="$localname"/></a> + </xsl:when> + <xsl:otherwise> + <xsl:variable name="definition" select="$grammars/descendant::xs:*[@name=$localname][ancestor-or-self::*[@targetNamespace=$ns-uri]]"/> + <a href="{$definition/ancestor-or-self::wadl:include[1]/@href}" title="{$definition/descendant::xs:documentation/descendant::text()}"><xsl:value-of select="$localname"/></a> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template name="expand-qname"> + <xsl:param name="context" select="."/> + <xsl:param name="qname"/> + <xsl:variable name="ns-uri"> + <xsl:call-template name="get-namespace-uri"> + <xsl:with-param name="context" select="$context"/> + <xsl:with-param name="qname" select="$qname"/> + </xsl:call-template> + </xsl:variable> + <xsl:text>{</xsl:text> + <xsl:value-of select="$ns-uri"/> + <xsl:text>} </xsl:text> + <xsl:value-of select="substring-after($qname, ':')"/> + </xsl:template> + + + <xsl:template name="representation-name"> + <xsl:variable name="expanded-name"> + <xsl:call-template name="expand-qname"> + <xsl:with-param select="@element" name="qname"/> + </xsl:call-template> + </xsl:variable> + <xsl:choose> + <xsl:when test="wadl:doc[@title]"> + <xsl:value-of select="wadl:doc[@title][1]/@title"/> + <xsl:if test="@status or @mediaType or @element"> (</xsl:if> + <xsl:if test="@status">Status Code </xsl:if><xsl:value-of select="@status"/> + <xsl:if test="@status and @mediaType"> - </xsl:if> + <xsl:value-of select="@mediaType"/> + <xsl:if test="(@status or @mediaType) and @element"> - </xsl:if> + <xsl:if test="@element"> + <abbr title="{$expanded-name}"><xsl:value-of select="@element"/></abbr> + </xsl:if> + <xsl:if test="@status or @mediaType or @element">)</xsl:if> + </xsl:when> + <xsl:otherwise> + <xsl:if test="@status">Status Code </xsl:if><xsl:value-of select="@status"/> + <xsl:if test="@status and @mediaType"> - </xsl:if> + <xsl:value-of select="@mediaType"/> + <xsl:if test="@element"> (</xsl:if> + <abbr title="{$expanded-name}"><xsl:value-of select="@element"/></abbr> + <xsl:if test="@element">)</xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- entity-encode markup for display --> + + <xsl:template match="*" mode="encode"> + <xsl:text><</xsl:text> + <xsl:value-of select="name()"/><xsl:apply-templates select="attribute::*" mode="encode"/> + <xsl:choose> + <xsl:when test="*|text()"> + <xsl:text>></xsl:text> + <xsl:apply-templates select="*|text()" mode="encode" xml:space="preserve"/> + <xsl:text></</xsl:text><xsl:value-of select="name()"/><xsl:text>></xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>/></xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="@*" mode="encode"> + <xsl:text> </xsl:text><xsl:value-of select="name()"/><xsl:text>="</xsl:text><xsl:value-of select="."/><xsl:text>"</xsl:text> + </xsl:template> + + <xsl:template match="text()" mode="encode"> + <xsl:value-of select="." xml:space="preserve"/> + </xsl:template> + + <!-- copy HTML for display --> + + <xsl:template match="html:*" mode="copy"> + <!-- remove the prefix on HTML elements --> + <xsl:element name="{local-name()}"> + <xsl:for-each select="@*"> + <xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute> + </xsl:for-each> + <xsl:apply-templates select="node()" mode="copy"/> + </xsl:element> + </xsl:template> + + <xsl:template match="@*|node()[namespace-uri()!='http://www.w3.org/1999/xhtml']" mode="copy"> + <!-- everything else goes straight through --> + <xsl:copy> + <xsl:apply-templates select="@*|node()" mode="copy"/> + </xsl:copy> + </xsl:template> + +</xsl:stylesheet>