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>&lt;</xsl:text>
<xsl:value-of
select="ancestor-or-self::*[self::element or self::attribute][@name][1]/@name"/>
<xsl:text>&gt;</xsl:text>
</xsl:template>

<xsl:template name="namesAttrsOfAncestorsOrSelf">
<xsl:call-template name="namesAttrsOfAncestors"/>
<xsl:if test="@name">
<xsl:text>&lt;</xsl:text><xsl:value-of select="@name"/><xsl:text>&gt;</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>&lt;</xsl:text><xsl:value-of select="."/><xsl:text>&gt;</xsl:text>
</xsl:for-each>
</xsl:template>

<xsl:template name="nameAttrOfDescendantOrSelf">
<xsl:text>&lt;</xsl:text>
<xsl:value-of
select="descendant-or-self::*[self::element or self::attribute][@name][1]/@name"/>
<xsl:text>&gt;</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(., '&#xA;')]) + 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>&#xA;</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]

Reply via email to