Good <user:time-of-day/>! I've been trying to go deeper into XForms and implement an xsl stylesheet that transforms a page from XForms markup into HTML. The following are random thoughts that were coming to me during it and they are accorded by the result of my try, consisting of a sample page in XForms format, a schema for instance data and the stylesheet used to display the page. (Just to remind what is XForms: it's an XML mark up format for the next generation of HTML forms. See: http://www.w3c.org/MarkUp/XForms)
1. Introduction A page in XForms format looks like this: <page> <model> <instance/> <schema/> <bind/>* <submitInfo/>* </model> <group/>* </page> The 'model' contains: instance data, schema (W3C Schema) for the instance data, bindings (=constraints) and submit info elements. Usually, all the parts, except 'instance' data are static, so static XML files can be used (although, it's possible to use XSP/JSP to generate dynamic forms and constraints). 2. It is for browsers! XForms is a client side form representation that should be processed by the browser, so it's not a format for server side processing. But some ideas from it can be used to implement server side processing of forms. How XForms can be implemented in web apps, particularly in Cocoon based applications? Two steps are required: display a page and handle submitted data using the same form model. 3. Displaying a form in XForms format. Usually, the process looks like this: (Form description + instance data) * transformer(s) = HTML Which means: transformer(s) takes the form description and the instance data then represent it in the target format. Normally, web applications use JavaBeans (or value objects in terms of EJB/J2EE) to hold the business data. So, the first task is rather simple: serialize JavaBeans/Value objects as XML. This can be easily solved with Castor and proposed CastorTransformer or a logicsheet. Transformation to HTML is a more complicated thing and requires dynamic XPath expressions support. I've been thinking of writing some JavaScript functions that can perform validation and visualization according to constraints, but that requires full support of XPath in JavaScript and some tricky things during transformation to place function calls in the needed places. To implement a simplies XSL for displaying a had to use Xalan extensions. For real XForms processing a special logicsheet or a transformer will be needed. In any case, this will require an intermediate markup format. 4. Processing submitted data. Procesing the submitted data requires following steps: validate user input against the Schema and constraints, map values to according JavaBeans/Value objects, perform the action (update, add, etc.). So, to validate the input against a Schema, request parameters should be translated to XML. For this purpose param names must be in XPath format (as it is going to be in XForms browsers), like this: customer/firstname=John customer/service[@id='prepaid']/discount=0.10 customer/address/line[1]=London, UK etc. As it were already proposed by Ivelin Ivanov, this XML can be unmarshalled to corresponding JavaBeans/Value objects using Castor and a special Cocoon action. After that binding constraints should be checked. And then a business logic action should be performed. 5. Sample This samples can be interesting to anybody who is interested in XForms. They demonstrate how an XForm can be displayed. Also, there is a demonstration of how 'relevant' and 'required' constraints can be visualized. To see how display style is changed, add/remove 'required' from bindings or change the expression in 'relevant' attribute. Also, in singleSelect element selectUI attribute can have values 'menu', 'listbox', 'radio', 'checkbox'. 6. Conclusion So, to conclude all the experience, I can state that for server-side XForms support will be needed some intermediate markup format, a pair of transformers/logicsheets, a mapping tool (like Castor) and formatting stylesheet(s). Any comments? Btw, is there any activity in ExFormula? Best regards, Konstantin Piroumian Software engineer Protek Flagship LLC Phone: + 7 095 795 0520 (add. 1288) Fax: + 7 095 795 0525 E-mail: [EMAIL PROTECTED] http://www.protek.com
<?xml version="1.0" encoding="UTF-8"?> <!-- XForms processing and displaying stylesheet. Author: Konstantin Piroumian, 21-Feb-2002 Email: [EMAIL PROTECTED] --> <!DOCTYPE html [ <!ENTITY nbsp " "> ]> <!-- NOTE: Xalan extensions are used to evaluate constraints and dynamic XPath expressions. This is not supported by standart specification. --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" exclude-result-prefixes="xalan" > <xsl:variable name="model" select="/page/model" /> <xsl:variable name="instance" select="$model/instance" /> <xsl:template match="page"> <html> <head> <title>Sample XForm</title> <style type="text/css"> <![CDATA[ h1 { font-size: large; color: #990000 } input { background-color: #FFFFFF; color: #000099 } table { background-color: #EEEEEE; color: #000099; font-size: x-small; } select { background-color: #FFFFFF; color: #000099 } .citizenship { color: #990000 } .table { border: 1px inset #999999;border: 1px inset #999999; width: 400px; } .sub-table { border: none; } .button { background-color: #FFFFFF; color: #000099; border: 1px solid #666666; width: 70px; } #US { background-color: #9999FF; color: #FF0000 } #CA { background-color: #FF0000; color: #FFFFFF } #RU { background-color: #FFFFFF; color: #0000FF } ]]> </style> </head> <body> <h1>Customer <i>#<xsl:value-of select="$instance/customer/@id" /></i> Personal Details Edit Form</h1> <!-- FIXME (KP): In what case we should generate a form tag and what if several submit elements are present? --> <form action="{$model/submitInfo/@action}" method="{$model/submitInfo/@method}"> <xsl:apply-templates /> </form> </body> </html> </xsl:template> <!-- Process an element group --> <xsl:template match="group"> <table> <xsl:call-template name="copy-common-attrs" /> <xsl:for-each select="*"> <tr> <xsl:if test="name() != 'submit'"> <td> <xsl:value-of select="caption" /> </td> </xsl:if> <xsl:if test="name() != 'submit'"> <td> <xsl:variable name="is-required"> <xsl:call-template name="is-required" /> </xsl:variable> <xsl:choose> <xsl:when test="$is-required='true'"> * </xsl:when> <xsl:otherwise> </xsl:otherwise> </xsl:choose> </td> </xsl:if> <td> <xsl:if test="name() = 'submit'"> <xsl:attribute name="colspan">3</xsl:attribute> <xsl:attribute name="align">center</xsl:attribute> </xsl:if> <xsl:apply-templates select="." /> </td> </tr> </xsl:for-each> </table> </xsl:template> <!-- Process a specific group. Normally, this kind of templates should be in a customized stylesheet. See: Customer.xsl --> <xsl:template match="group[@id='fullname']"> <div> <xsl:call-template name="copy-common-attrs" /> <xsl:for-each select="*"> <xsl:value-of select="caption" /><br/> <xsl:apply-templates select="." /> <br /> </xsl:for-each> </div> </xsl:template> <xsl:template match="output"> <xsl:call-template name="get-ref-value" /> </xsl:template> <xsl:template match="submit"> <xsl:variable name="submitInfo" select="@submitInfo" /> <input type="submit" name="{@submitInfo}" value="{caption}"> <xsl:call-template name="copy-common-attrs" /> </input> </xsl:template> <xsl:template match="input"> <xsl:variable name="is-relevant"> <xsl:call-template name="is-relevant" /> </xsl:variable> <input name="{@ref}" type="text" title="{hint}"> <xsl:attribute name="value"><xsl:call-template name="get-ref-value" /></xsl:attribute> <xsl:call-template name="copy-common-attrs" /> <!-- This is a demonstration 'relevant' constraint usage --> <xsl:if test="$is-relevant!='true'"> <xsl:attribute name="style">background-color:#CCCCCC; color: #999999</xsl:attribute> </xsl:if> </input> </xsl:template> <xsl:template match="singleSelect[@selectUI='menu']"> <xsl:variable name="selected"> <xsl:call-template name="get-ref-value" /> </xsl:variable> <select name="{@ref}"> <xsl:call-template name="copy-common-attrs" /> <xsl:for-each select="item"> <option value="{value}"> <xsl:call-template name="copy-common-attrs" /> <xsl:if test="$selected=value"> <xsl:attribute name="selected">selected</xsl:attribute> </xsl:if> <xsl:value-of select="caption" /> </option> </xsl:for-each> </select> </xsl:template> <xsl:template match="singleSelect[@selectUI='radio']"> <xsl:variable name="selected"> <xsl:call-template name="get-ref-value" /> </xsl:variable> <xsl:for-each select="item"> <input type="radio" name="{../@ref}" > <xsl:call-template name="copy-common-attrs" /> <xsl:if test="$selected=value"> <xsl:attribute name="checked">checked</xsl:attribute> </xsl:if> </input> <xsl:value-of select="caption" /><br/> </xsl:for-each> </xsl:template> <xsl:template match="singleSelect[@selectUI='listbox']"> <xsl:variable name="selected"> <xsl:call-template name="get-ref-value" /> </xsl:variable> <select name="{@ref}" size="{count(item)}"> <xsl:call-template name="copy-common-attrs" /> <xsl:for-each select="item"> <option value="{value}"> <xsl:call-template name="copy-common-attrs" /> <xsl:if test="$selected=value"> <xsl:attribute name="selected">selected</xsl:attribute> </xsl:if> <xsl:value-of select="caption" /> </option> </xsl:for-each> </select> </xsl:template> <xsl:template match="singleSelect[@selectUI='checkbox']"> <xsl:variable name="selected"> <xsl:call-template name="get-ref-value" /> </xsl:variable> <xsl:variable name="check"> <xsl:call-template name="string-to-boolean"> <xsl:with-param name="value" select="$selected" /> </xsl:call-template> </xsl:variable> <xsl:for-each select="item"> <input type="checkbox" name="{../@ref}" > <xsl:call-template name="copy-common-attrs" /> <xsl:if test="$check='true'"> <xsl:attribute name="checked">checked</xsl:attribute> </xsl:if> </input> <xsl:value-of select="caption" /><br/> </xsl:for-each> </xsl:template> <!-- Helper named templated --> <!-- This template constructs full path to instance data node for the context node using recursion and values of 'ref' attributes --> <xsl:template name="get-ref-path"> <xsl:param name="path" /> <xsl:param name="node" select="." /> <xsl:choose> <xsl:when test="not($node/@ref)"> <xsl:value-of select="$path" /> </xsl:when> <xsl:when test="string($node/@ref) and string($path)"> <xsl:call-template name="get-ref-path"> <xsl:with-param name="path" select="concat($node/@ref, '/', $path)" /> <xsl:with-param name="node" select="$node/.." /> </xsl:call-template> </xsl:when> <xsl:when test="$node/@ref"> <xsl:call-template name="get-ref-path"> <xsl:with-param name="path" select="@ref" /> <xsl:with-param name="node" select="$node/.." /> </xsl:call-template> </xsl:when> </xsl:choose> </xsl:template> <xsl:template name="get-ref-value"> <xsl:variable name="ref"> <xsl:call-template name="get-ref-path" /> </xsl:variable> <xsl:value-of select="xalan:evaluate(concat('/page/model/instance/', $ref))" /> </xsl:template> <xsl:template name="get-bind"> <xsl:param name="path"> <xsl:call-template name="get-ref-path" /> </xsl:param> <xsl:value-of select="$model/bind[@ref=$path]" /> </xsl:template> <xsl:template name="is-required"> <xsl:param name="path"> <xsl:call-template name="get-ref-path" /> </xsl:param> <xsl:value-of select="$model/bind[@ref=$path]/@required='true'" /> </xsl:template> <xsl:template name="is-relevant"> <xsl:param name="path"> <xsl:call-template name="get-ref-path" /> </xsl:param> <xsl:variable name="expr" select="$model/bind[@ref=$path]/@relevant" /> <xsl:choose> <xsl:when test="$expr"> <xsl:value-of select="xalan:evaluate(concat('/page/model/instance/', $expr))" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="true()" /> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="copy-common-attrs"> <xsl:copy-of select="@class | @title | @id" /> </xsl:template> <xsl:template name="string-to-boolean"> <xsl:param name="value">false</xsl:param> <xsl:value-of select="$value='true' or $value='yes'" /> </xsl:template> <xsl:template match="node()"></xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> <!-- edited with XML Spy v4.3 U (http://www.xmlspy.com) by Konstantin Piroumian (Protek) --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xs:element name="customer"> <xs:annotation> <xs:documentation>Customer info</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="firstname" type="personName"/> <xs:element name="lastname" type="personName"/> <xs:element name="citizenship" type="countryCode"/> <xs:element name="has-car" type="xs:boolean" default="false"/> <xs:element name="car-info" type="car-infoType"/> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="type" type="xs:ID" use="required"/> </xs:complexType> </xs:element> <xs:simpleType name="personName"> <xs:restriction base="xs:string"> <xs:maxLength value="1"/> <xs:pattern value="\c{20}"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="countryCode"> <xs:restriction base="xs:string"> <xs:pattern value="[A_ZA_Z]"/> <xs:enumeration value="RU"/> <xs:enumeration value="US"/> <xs:enumeration value="CA"/> <xs:enumeration value="GB"/> </xs:restriction> </xs:simpleType> <xs:complexType name="car-infoType"> <xs:sequence> <xs:element name="issue-year" type="xs:gYear"/> <xs:element name="model" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema>
<?xml version="1.0" encoding="UTF-8"?> <!-- XForms based form description sample. Author: Konstantin Piroumian, 21-Feb-2002 Email: [EMAIL PROTECTED] --> <!-- Root element of the document. Can be in HTML, XHTML or some other namespace --> <page> <!-- Data model used in this form --> <model xmlns:xlink="http://foo.com/xlink"> <!-- XML Schema for the instance data --> <schema xlink:href="XForms.xsd"/> <!-- Instance data: initial values --> <instance> <customer id="102038" type="individual"> <firstname>John</firstname> <lastname>Lennon</lastname> <citizenship>CA</citizenship> <has-car>false</has-car> <car-info> <issue-year>1999</issue-year> <model>Volkswagen Passat</model> </car-info> </customer> </instance> <!-- Data binding with simple constraints --> <bind ref="customer/@id" readOnly="true"/> <bind ref="customer/firstname" required="true"/> <bind ref="customer/lastname" required="true"/> <bind ref="customer/citizenship" relevant="customer/@type='individual'"/> <bind ref="customer/car-info/issue-year" relevant="customer/has-car='true'" required="true" /> <bind ref="customer/car-info/model" relevant="customer/has-car='true'" required="true" /> <!-- What and where to submit --> <submitInfo action="http://example.com/submit" method="post" id="s00"/> </model> <!-- The form UI definition itself. Normally, it can be mixed with other markup: HTML, WML or other to provide a customized layout --> <group ref="customer" class="table"> <output ref="@id"> <caption>Customer ID</caption> </output> <input ref="firstname"> <caption>First name</caption> <hint>Your first name</hint> </input> <input ref="lastname"> <caption>Last name</caption> <hint>Your last name</hint> </input> <singleSelect ref="citizenship" selectUI="menu" selection="closed" class="citizenship"> <caption>Citizenship</caption> <item id="US"> <caption>United States</caption> <value>US</value> </item> <item id="CA"> <caption>Canada</caption> <value>CA</value> </item> <item id="RU"> <caption>Russian Federation</caption> <value>RU</value>4 </item> </singleSelect> <singleSelect ref="has-car" selectUI="checkbox"> <caption>Own a car?</caption> <item> <caption>(Yes or No)</caption> </item> </singleSelect> <group ref="car-info" class="sub-table"> <input ref="issue-year"> <caption>Car Issue Year</caption> <hint>The year when the car was issued</hint> </input> <input ref="model"> <caption>Car Model</caption> <hint>Model of your car</hint> </input> </group> <!-- This can be displayed as a submit button, a link or somehow else --> <submit submitInfo="s00" class="button"> <caption>Save</caption> </submit> </group> </page>
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]