This is kind of rambling, description of what I'm doing with this RNG
roundtrip stuff and then a question for guidance about what tools to
use next.
I'm making progress on my relax ng roundtrip code. I've copied in my
XSLT at the bottom. It's a work in progress, only half-finished so I'm
attaching it for your amusement and edification ;-). The input should
be a valid RNG grammar. I'm generating mine using dtdinst, so there's
probably areas of RNG that I don't cover because dtdinst doesn't make
them. The source I'm using is "Resume XML" from sf.net. To actually use
it you'd have to pipe it into another stylesheet that outputs proper
HTML.
I've got it working pretty well in terms of being able to navigate
through a representation of the schema as HTML forms, add items where
multiples are allowed by passing back XPath for a subtree of the RNG
and generating a new page, and grabbing a piece of input from the user
using HTML forms parameters.
Now, I need to assemble an instance document from these pieces. The
instance document should validate to the grammar. The instance document
is going to come in bits and pieces as the user navigates the forms and
fills things in. I'd like to be able to display the partially complete
instance among the forms as well (with edit and delete). The central
issue is assembling the pieces on the back end. I imagine I'll need to
use DOM, perhaps writing out a file each time, reading it back in,
figuring out where the new content goes (the forms know where they are
from the RNG) and inserting it into the DOM tree somehow.
My question is, what tool(s) should I look at for doing this? AFAICT, I
have a bunch of different choices. One is that I could mess around with
XSLT, adding to the round-trip a partially assembled instance document,
maybe as part of a big textarea at the bottom. Another I think might be
possible is, I could go into XSP and use some sort of taglib that it
looks like I would have to write. If I write a taglib, it seems as
though I would probably use XML::LibXML::DOM in some way. One thing
that struck me as I was reading about that (or some similar module) was
that it had the ability to automatically validate based on a schema,
which could be really useful. It would be nice if I could delegate
identifying errors to some existing code, and just concentrate on
displaying them properly. Or maybe there's another way that I've
overlooked right now (I /don't/ think a provider is necessary).
TIA,
simon
<?xml version="1.0"?>
<!--
This XSLT is intended to support roundtrip of XML data
generated from and validating to an RNG schema.
It generates a page from a Relax NG (RNG) Schema which is
the source XML file. The page consists of HTML forms.
The intention is to construct an XML document from the values
entered into the forms by the user, into an XML document
that conforms to the RNG schema.
-->
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:dyn="http://exslt.org/dynamic"
extension-element-prefixes="dyn"
>
<!--[[xsl:strip-space elements="*" /]]-->
<!--apparently that doesn't play well with the linenumber finding
scheme I have here-->
<xsl:output omit-xml-declaration = "yes"/>
<xsl:param name="StartPointStr">//grammar</xsl:param>
<xsl:param name="StartNode" select="dyn:evaluate($StartPointStr)"/>
<xsl:param name="AddXPath">DEFAULT</xsl:param>
<xsl:param name="AddType">DEFAULT</xsl:param>
<xsl:param name="AddContent">DEFAULT</xsl:param>
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--Normal templates -->
<!--******************************-->
<!--******************************-->
<!--Root block-->
<xsl:template match="grammar">
<div class="rngform">
<h1>A Form</h1>
<xsl:if test="$StartPointStr != '//grammar'">
<h2>
<xsl:value-of select="$StartPointStr"/>
</h2>
</xsl:if>
<xsl:if test="$AddXPath != 'DEFAULT'">
<p>
You added <b><xsl:value-of select="$AddContent"/></b>
of type <b><xsl:value-of select="$AddType"/></b>
into the location <b><xsl:value-of select="$AddXPath"/></b>.
</p>
</xsl:if>
<xsl:apply-templates/>
</div> <!--rngform-->
</xsl:template>
<xsl:template match="start">
<xsl:choose>
<xsl:when test="$StartPointStr != '//grammar'">
<xsl:apply-templates select="$StartNode"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--******************************-->
<!--Conditional blocks-->
<xsl:template match="choice">
<br />
CHOICE:
<div class="choice">
<xsl:apply-templates mode="choice"/>
</div>
</xsl:template>
<xsl:template match="choice/*" mode="choice">
<div class="choice">
<input type="radio">
<xsl:attribute name="name">
<xsl:call-template name="namesAttrsOfAncestors"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="./@name"/>
</xsl:attribute>
</input>
<b>
<xsl:call-template name="namesAttrsOfAncestors"/>
<xsl:choose>
<xsl:when test="./@name">
[<xsl:value-of select="./@name"/>]
</xsl:when>
<xsl:otherwise>
[<xsl:value-of select="child::text()"/>]
</xsl:otherwise>
</xsl:choose>
</b>
<xsl:apply-templates select="."/>
</div>
</xsl:template>
<xsl:template match="optional">
<br />
<input type="checkbox">
<xsl:attribute name="name">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
<xsl:call-template name="nameAttrOfDescendantOrSelf"/>
</xsl:attribute>
</input>
<div class="optional">
<div class="optionaltitle">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
<xsl:call-template name="nameAttrOfDescendantOrSelf"/>
</div>
<xsl:call-template name="simpleRecurse"/>
</div><!--optional-->
</xsl:template>
<!--******************************-->
<!--Dynamic blocks-->
<xsl:template match="zeroOrMore">
<br />
ZERO OR MORE:
<div class="zeroormore">
<xsl:call-template name="simpleRecurse"/>
</div>
</xsl:template>
<xsl:template match="oneOrMore">
<br />
ONE OR MORE:
<xsl:call-template name="simpleRecurse"/>
</xsl:template>
<!--******************************-->
<!--Basic blocks-->
<xsl:template match="attribute">
<xsl:choose>
<xsl:when test="not(./*) or ./empty"> <!--no children-->
<div class="nochild">
<br />
<xsl:call-template name="inputTemplate"/>
(<xsl:call-template name="labelTemplate"/>)
</div>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="simpleRecurse"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="element">
<xsl:if test="not(text)">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</xsl:if>
<xsl:choose>
<xsl:when
test="$StartNode//zeroOrMore//*[generate-id()=generate-id(current())]
or
$StartNode//oneOrMore//*[generate-id()=generate-id(current())]
">
<!--Stop here and allow the user to expand this section-->
<form action="form.html" method="GET">
<button type="submit" name="StartPointStr">
<xsl:attribute name="value">
<xsl:call-template name="thepath"/>
</xsl:attribute>
<xsl:text>Add One of These</xsl:text>
</button>
<i><xsl:call-template name="thepath"/></i>
</form>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="simpleRecurse"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--******************************-->
<!--Specific blocks-->
<xsl:template match="value">
<!--????-->
</xsl:template>
<xsl:template match="text">
<div class="nochild">
<form action="form.html" method="GET">
<xsl:call-template name="textareaTemplate"/>
(<xsl:call-template name="labelTemplate"/> (text))
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</form>
</div>
</xsl:template>
<xsl:template match="data">
<div class="nochild">
<form action="form.html" method="GET">
<xsl:call-template name="inputTemplate"/>
(<xsl:call-template name="labelTemplate"/>
(<xsl:value-of select="@type"/>))
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</form>
</div>
<!--should check for param children-->
</xsl:template>
<xsl:template match="group">
<!--TODO-->
</xsl:template>
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--******************************-->
<!--Named templates -->
<!--******************************-->
<!--******************************-->
<!--HTML Forms labels and inputs-->
<xsl:template name="labelTemplate">
<label>
<xsl:attribute name="for">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</xsl:attribute>
<b>
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</b>
</label>
</xsl:template>
<xsl:template name="inputTemplate">
<xsl:call-template name="formVarsTemplate"/>
<input name="AddContent">
<xsl:attribute name="value">
<xsl:call-template name="nameAttrOfAncestorOrSelf"/>
</xsl:attribute>
</input>
</xsl:template>
<xsl:template name="textareaTemplate">
<xsl:call-template name="formVarsTemplate"/>
<textarea name="AddContent">
<xsl:attribute name="value">
<xsl:call-template name="nameAttrOfAncestorOrSelf"/>
</xsl:attribute>
</textarea>
</xsl:template>
<xsl:template name="formVarsTemplate">
<input type="hidden" name="AddXPath">
<xsl:attribute name="value">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</xsl:attribute>
</input>
<input type="hidden" name="AddType">
<xsl:attribute name="value">
<xsl:choose>
<xsl:when test="@type">
<xsl:value-of select="@type"/>
</xsl:when>
<xsl:otherwise>text</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</input>
</xsl:template>
<!--******************************-->
<!--HTML Forms radio buttons-->
<xsl:template name="radioTemplate">
<input type="radio">
<xsl:attribute name="name">
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:value-of select="."/>
</xsl:attribute>
</input>
<b>
<xsl:call-template name="namesAttrsOfAncestorsOrSelf"/>
</b>
</xsl:template>
<!--******************************-->
<!--Names of ancestors/descendants-->
<xsl:template name="nameAttrOfAncestorOrSelf">
<xsl:text><</xsl:text>
<xsl:value-of
select="ancestor-or-self::*[self::element or
self::attribute][@name][1]/@name"/>
<xsl:text>></xsl:text>
</xsl:template>
<xsl:template name="namesAttrsOfAncestorsOrSelf">
<xsl:call-template name="namesAttrsOfAncestors"/>
<xsl:if test="@name">
<xsl:text><</xsl:text><xsl:value-of
select="@name"/><xsl:text>></xsl:text>
</xsl:if>
</xsl:template>
<xsl:template name="namesAttrsOfAncestors">
<xsl:for-each
select="ancestor::*/@name">
<!--select="ancestor::*[self::element or
self::attribute][@name]/@name"]]-->
<xsl:text><</xsl:text><xsl:value-of
select="."/><xsl:text>></xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="nameAttrOfDescendantOrSelf">
<xsl:text><</xsl:text>
<xsl:value-of
select="descendant-or-self::*[self::element or
self::attribute][@name][1]/@name"/>
<xsl:text>></xsl:text>
</xsl:template>
<!--******************************-->
<!--Misc-->
<xsl:template name="simpleRecurse">
<xsl:call-template name="sourceLineNumberTemplate"/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="sourceLineNumberTemplate">
<!--For debugging purposes-->
<xsl:text> </xsl:text>
<xsl:text>{{{</xsl:text>
<!--[[xsl:value-of select="(count(preceding::* | ancestor::*) * 2 *
9 div 10 + 1" /]]-->
<!--works (less well) even if whitespace stripping is on-->
<xsl:value-of select="count(preceding::text()[contains(.,
'
')]) + 1"/>
<xsl:text>}}}</xsl:text>
</xsl:template>
<xsl:template match="define">
<!--[Define: [[b]][[xsl:value-of select="@name"/]][[/b]]]-->
</xsl:template>
<!--Some Magic from Dimitre that generates the XPath
of the context node! -->
<xsl:template name="thepath">
<xsl:variable name="theResult">
<xsl:variable name="theNode" select="."/>
<xsl:for-each select="$theNode |
$theNode/ancestor-or-self::node()[..]">
<xsl:element name="slash">/ </xsl:element>
<xsl:choose>
<xsl:when test="self::*">
<xsl:element name="nodeName">
<xsl:value-of select="name()"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::*[name(current()) =
name()])"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::*[name(current()) =
name()])"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition + 1,
']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:otherwise> <!-- This node is not an element -->
<xsl:choose>
<xsl:when test="count(. | ../@*) = count(../@*)">
<!-- Attribute -->
<xsl:element name="nodeName">
<xsl:value-of select="concat('@',name())"/>
</xsl:element>
</xsl:when>
<xsl:when test="self::text()">
<!-- Text -->
<xsl:element name="nodeName">
<xsl:value-of select="'text()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::text())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::text())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition +
1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<!-- Processing Instruction -->
<xsl:element name="nodeName">
<xsl:value-of select="'processing-instruction()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::processing-instruction())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::processing-instruction())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition +
1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::comment()">
<!-- Comment -->
<xsl:element name="nodeName">
<xsl:value-of select="'comment()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::comment())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::comment())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select="concat('[', $thisPosition +
1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="count(. | ../namespace::*) =
count(../namespace::*)">
<!-- Namespace: -->
<xsl:variable name="apos">'</xsl:variable>
<xsl:element name="nodeName">
<xsl:value-of select="concat('namespace::*',
'[local-name() = ', $apos, local-name(), $apos,
']')"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:value-of select="$theResult"/>
</xsl:template>
</xsl:stylesheet>
---
www.simonwoodside.com
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
