Thanks for the good work Chris.
I will try to review the code soon.


-=Ivelin=-
----- Original Message -----
From: "Christopher Oliver" <[EMAIL PROTECTED]>
To: <[EMAIL PROTECTED]>
Sent: Tuesday, February 18, 2003 5:21 PM
Subject: XMLForm/Flow Integration


> Attached is a first try at integrating XMLForm with the Cocoon Flow
> layer. Since I'm not an expert on XMLForm or XForms  (or Cocoon for that
> matter!) it's likely to have limitations or defects so keep that in mind.
>
> The attached files provide an implementation of the XMLForm Feedback
> Wizard sample, but any XMLForm application should be easy to convert to
> use the Flow layer instead of actions. Here is the flow version of the
> Feedback Wizard sample (see feedbackWizard.js):
>
> // XML Form Feedback Wizard Application
>
> importPackage(Packages.org.apache.cocoon.samples.xmlform);
>
> function feedbackWizard(xform) {
>     xform.setModel(new UserBean());
>     xform.sendView("userIdentity", "wizard/userIdentity.xml");
>     print("handling user identity");
>     xform.sendView("deployment", "wizard/deployment.xml");
>     print("handling deployment");
>     xform.sendView("system", "wizard/system.xml");
>     print("handling system");
>     xform.sendView("confirm", "wizard/confirm.xml");
>     print("handling confirm");
>     xform.finish("end", "wizard/end.xml");
>     print("done");
> }
>
>
> To try the flow version of the feedback wizard, do the following:
>
> 1) Save xmlform.js, feedbackWizard.js, and sitemap.xmap in
> src/webapp/samples/xmlform
> 2) Save confirm.xml, deployment.xml, end.xml, start.xml, system.xml. and
> useIdentity.xml in src/webapp/samples/xmlform/wizard
> 3) Save  XMLFormTransformer.java in
> src/java/org/apache/cocoon/transformation/
>
> Below is description of what I did (and what you'd need to do to migrate
> your own XMLForm application).
>
> Let me know if you have problems running the sample and give me your
> feedback on this approach to integrating XMLForm and Flow.
>
> Regards,
>
> Chris
>
>
> 1) Write a JavaScript function that represents your page flow (see
> feedbackWizard.js for an example). This function will be called with one
> parameter which is a JavaScript version of the XMLForm object. This
> object has the following methods:
>
>               XForm.setModel(bean)  // Assign "bean" as the model for
> this form
>               XForm.sendView(view, uri)  // Sends "view" to the
> presentation pipeline and waits for the form to be submitted (and
> automatically resends it if validation fails)
>               XForm.finish(view, uri)  // Sends "view" to the
> presentation pipeline but doesn't block
>
> Internally, this object delegates to an instance of
> org.apache.components.xmlform.Form and uses standard XMLForm
> functionality. Its implementation is in the attached "xmlform.js".
>
> 2) To support bidirectional navigation in your forms simply modify your
> XMLForm document's <xf:submit> elements to include an additional
> attribute called "continuation". The value for this attribute should be
> "back" to go back or "forward" to go forward.  I modified
> XMLFormTransformer (see attached) to substitute the proper continuation
> id for the "id" attribute in <xf:submit> when the "continuation"
> attribute is present. I've attached accordingly modified versions of the
> XMLForm Feedback wizard documents. For example, here's the relevant
> snippet from "system.xml":
>
>    <xf:submit id="prev" continuation="back" class="button">
>       <xf:caption>Prev</xf:caption>
>       <xf:hint>Go to previous page</xf:hint>
>     </xf:submit>
>     <xf:submit id="next" continuation="forward" class="button">
>       <xf:caption>Next</xf:caption>
>       <xf:hint>Go to next page</xf:hint>
>     </xf:submit>
>   </xf:form>
>
> 3) Modify your sitemap to call the "xmlForm" Flow function instead of
> your Action. Here are the relevant snippets from the attached sitemap
>
> <!-- Add an entry for the flow layer to load your scripts  -->
>   <map:flow language="JavaScript">
>       <map:script src="xmlform.js"/>
>       <map:script src="feedbackWizard.js"/>
>     </map:flow>
>
> <!-- Add a pipeline that calls the "xmlForm" Flow function instead of an
> action. This takes the same parameters as an XMLForm action -->
> <!-- In addition, it takes one additional parameter (xml-form-function)
> which is the name of the application function from (1) above -->
> <!-- Note: you must specify the parameters in the order below! -->
>
>       <map:match pattern="wizard">
>         <map:call function="xmlForm">
>             <map:parameter name="xml-form-function"
value="feedbackWizard"/>
>             <map:parameter name="xml-form-id" value="form-feedback"/>
>             <map:parameter name="xml-form-validator-schema-ns"
> value="http://www.ascc.net/xml/schematron"/>
>             <map:parameter name="xml-form-validator-schema"
> value="schematron/wizard-xmlform-sch-report.xml"/>
>             <map:parameter name="xml-form-scope" value="session"/>
>         </map:call>
>       </map:match>
>
> <!-- move the presentation pipeline out of the action so it can be
> called from the flow layer -->
>       <map:match pattern="wizard/*.xml">
>           <!-- original XMLForm document -->
>           <map:generate src="wizard/{1}.xml"/>
>
>           <!-- populating the document with model instance data -->
>           <map:transform type="xmlform"  label="xml"/>
>
>           <!-- personalizing the look and feel of the form controls  -->
>           <map:transform type="xalan" src="stylesheets/wizard2html.xsl" />
>
>           <!-- Transforming the XMLForm controls to HTML controls -->
>           <map:transform
> src="context://samples/stylesheets/xmlform/xmlform2html.xsl" />
>
>           <!-- sending the HTML back to the browser -->
>           <map:serialize type="html" label="debug"/>
>     </map:match>
>     </map:pipeline>
>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0"?>
> <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0";>
>
>   <!-- =========================== Components
================================ -->
>   <map:components>
>     <map:actions>
>       <map:action name="WizardAction"
src="org.apache.cocoon.samples.xmlform.WizardAction"
logger="xmlform.sitemap.action.wizard"/>
>       <map:action name="UsageFeedbackAction"
src="org.apache.cocoon.samples.xmlform.UsageFeedbackAction"
logger="xmlform.sitemap.action.UsageFeedback"/>
>     </map:actions>
>     <map:flow-interpreters default="JavaScript"/>
>     <map:serializers default="html"/>
>     <map:matchers default="wildcard"/>
>   </map:components>
>
>
> <!-- =========================== Views
=================================== -->
>
> <!--
>   The debug view can be used to output an intermediate
>   snapshot of the pipeline.
>   Pass cocoon-view=debug as a URL parameter to see
>   the pipeline output produced by the transofrmer
>   labeled "debug". You can move the label to different
>   transformers to understand each processing
>   stage better.
> -->
> <map:views>
>   <map:view name="debug" from-label="debug">
>     <map:serialize type="xml"/>
>   </map:view>
>   <map:view name="xml" from-label="xml">
>     <map:serialize type="xml"/>
>   </map:view>
> </map:views>
>
>   <!-- =========================== Resources
================================= -->
>
>   <map:resources>
>   </map:resources>
>
>   <!-- =========================== Pipelines
================================= -->
>     <map:flow language="JavaScript">
>       <map:script src="xmlform.js"/>
>       <map:script src="feedbackWizard.js"/>
>     </map:flow>
>
>   <map:pipelines>
>
>     <map:pipeline>
>
>       <map:match pattern="">
>         <map:redirect-to uri="overview.html"/>
>       </map:match>
>
>       <map:match pattern="overview.html">
>        <map:read src="overview.html"/>
>       </map:match>
>
>     </map:pipeline>
>
>     <map:pipeline>
>
>       <map:match pattern="wizard">
>         <map:call function="xmlForm">
> <map:parameter name="xml-form-function" value="feedbackWizard"/>
> <map:parameter name="xml-form-id" value="form-feedback"/>
>         <map:parameter name="xml-form-validator-schema-ns"
value="http://www.ascc.net/xml/schematron"/>
>         <map:parameter name="xml-form-validator-schema"
value="schematron/wizard-xmlform-sch-report.xml"/>
>         <map:parameter name="xml-form-scope" value="session"/>
>         </map:call>
>       </map:match>
>
>       <map:match pattern="wizard/*.xml">
>       <!-- original XMLForm document -->
>       <map:generate src="wizard/{1}.xml"/>
>
>       <!-- populating the document with model instance data -->
>       <map:transform type="xmlform"  label="xml"/>
>
>       <!-- personalizing the look and feel of the form controls  -->
>       <map:transform type="xalan" src="stylesheets/wizard2html.xsl" />
>
>       <!-- Transforming the XMLForm controls to HTML controls -->
>       <map:transform
src="context://samples/stylesheets/xmlform/xmlform2html.xsl" />
>
>       <!-- sending the HTML back to the browser -->
>       <map:serialize type="html" label="debug"/>
> </map:match>
>     </map:pipeline>
>
>     <map:pipeline>
>
>       <!-- A non-trivial interactive example - Cocoon Usage Feedback
Wizard -->
>       <map:match pattern="owizard*">
>         <map:act type="WizardAction">
>
>           <!-- XMLForm parameters for the AbstractXMLFormAction -->
>           <map:parameter name="xmlform-validator-schema-ns"
value="http://www.ascc.net/xml/schematron"/>
>           <map:parameter name="xmlform-validator-schema"
value="schematron/wizard-xmlform-sch-report.xml"/>
>           <map:parameter name="xmlform-id" value="form-feedback"/>
>           <map:parameter name="xmlform-scope" value="session"/>
>           <map:parameter name="xmlform-model"
value="org.apache.cocoon.samples.xmlform.UserBean"/>
>
>
>       <!-- original XMLForm document -->
>       <map:generate src="wizard/{page}.xml"/>
>
>       <!-- populating the document with model instance data -->
>       <map:transform type="xmlform"  label="xml"/>
>
>       <!-- personalizing the look and feel of the form controls  -->
>       <map:transform type="xalan" src="stylesheets/wizard2html.xsl" />
>
>       <!-- Transforming the XMLForm controls to HTML controls -->
>       <map:transform
src="context://samples/stylesheets/xmlform/xmlform2html.xsl" />
>
>       <!-- sending the HTML back to the browser -->
>       <map:serialize type="html" label="debug"/>
>
>         </map:act>
>       </map:match>
>     </map:pipeline>
>
>     <map:pipeline>
>
>       <!-- The same application, exposed as a Web Service (REST style) -->
>
>
>       <!-- WSDL descriptor -->
>       <map:match pattern="UsageFeedbackService/WSDL">
>           <map:generate src="webservice/usagefeedback.wsdl"/>
>           <map:serialize type="xml"/>
>   </map:match>
>
>
>       <!-- The REST resource (or Web Service) -->
>       <map:match pattern="UsageFeedbackService">
>         <map:act type="UsageFeedbackAction">
>
>           <!-- Parameters for the AbstractXMLFormAction -->
>           <map:parameter name="xmlform-validator-schema-ns"
value="http://www.ascc.net/xml/schematron"/>
>           <map:parameter name="xmlform-validator-schema"
value="schematron/wizard-xmlform-sch-report.xml"/>
>           <map:parameter name="xmlform-id" value="form-feedback"/>
>           <map:parameter name="xmlform-scope" value="request"/>
>           <map:parameter name="xmlform-model"
value="org.apache.cocoon.samples.xmlform.UserBean"/>
>
>           <!-- Response content and transformation logic -->
>           <map:generate src="webservice/{page}.xml"/>
>           <map:transform type="xmlform"  label="debug, xml"/>
>           <map:serialize type="xml"/>
>         </map:act>
>       </map:match>
>     </map:pipeline>
>
>
>   </map:pipelines>
>
>
> </map:sitemap>
> <!-- end of file -->
>
>
>
>
>
>
>
>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0"?>
> <!--
>
> XMLForm instance document for the Cocoon Feedback Wizard.
>
>   author: Torsten Curdt, [EMAIL PROTECTED], March 2002
>     author: Ivelin Ivanov, [EMAIL PROTECTED], April 2002
>     author: Simon Price <[EMAIL PROTECTED]>, September 2002
>
> -->
> <document xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";>
>   <xf:form id="form-feedback" view="confirm" action="wizard">
>     <xf:caption>Confirm Input</xf:caption>
>     <!-- from page1 -->
>     <xf:output ref="firstName">
>       <xf:caption>First Name</xf:caption>
>     </xf:output>
>     <xf:output ref="lastName">
>       <xf:caption>Last Name</xf:caption>
>     </xf:output>
>     <xf:output ref="email">
>       <xf:caption>Email</xf:caption>
>     </xf:output>
>     <xf:output ref="age">
>       <xf:caption>Age</xf:caption>
>       <xf:violations class="error"/>
>     </xf:output>
>     <xf:group ref="/">
>       <xf:caption>Professional roles</xf:caption>
>       <xf:repeat nodeset="role">
>         <xf:output ref="."/>
>       </xf:repeat>
>     </xf:group>
>     <xf:group ref="/">
>       <xf:caption>Personal hobbies</xf:caption>
>       <xf:repeat nodeset="hobby">
>         <xf:output ref="."/>
>       </xf:repeat>
>     </xf:group>
>     <xf:output ref="hidden">
>       <xf:caption>Hidden attribute</xf:caption>
>     </xf:output>
>     <!-- from page2 -->
>     <xf:output ref="number">
>       <xf:caption>Number of installations</xf:caption>
>     </xf:output>
>     <xf:output ref="liveUrl">
>       <xf:caption>Live URL</xf:caption>
>     </xf:output>
>     <xf:output ref="publish">
>       <xf:caption>Publish URL</xf:caption>
>     </xf:output>
>     <!-- from page3 -->
>     <xf:output ref="system/os">
>       <xf:caption>OS</xf:caption>
>     </xf:output>
>     <xf:output ref="system/processor">
>       <xf:caption>Processor</xf:caption>
>     </xf:output>
>     <xf:output ref="system/@ram">
>       <xf:caption>RAM</xf:caption>
>     </xf:output>
>     <xf:output ref="system/servletEngine">
>       <xf:caption>Servlet Engine</xf:caption>
>     </xf:output>
>     <xf:output ref="system/javaVersion">
>       <xf:caption>Java Version</xf:caption>
>     </xf:output>
>     <xf:group ref="/" id="favorites_group">
>       <xf:caption>Favorite web sites</xf:caption>
>       <xf:repeat nodeset="favorite[position() &lt;= 3]" id="favorites">
>         <xf:output ref="." class="info">
>           <xf:caption>URL: </xf:caption>
>         </xf:output>
>       </xf:repeat>
>     </xf:group>
>     <!-- submit -->
>     <xf:submit continuation="back" class="button">
>       <xf:caption>Prev</xf:caption>
>       <xf:hint>Go to previous page</xf:hint>
>     </xf:submit>
>     <xf:submit continuation="forward" class="button">
>       <xf:caption>Finish</xf:caption>
>       <xf:hint>Finish the wizard</xf:hint>
>     </xf:submit>
>   </xf:form>
>   <xf:output ref="count" id="show_count" form="form-feedback"
class="info">
>     <xf:caption>Visits Count</xf:caption>
>   </xf:output>
> </document>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0"?>
> <!--
>
> XMLForm instance document for the Cocoon Feedback Wizard.
>
>   author: Torsten Curdt, [EMAIL PROTECTED], March 2002
>     author: Ivelin Ivanov, [EMAIL PROTECTED], April 2002
>     author: Simon Price <[EMAIL PROTECTED]>, September 2002
>
> -->
> <document xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";>
> <xf:form id="form-feedback" view="deployment" action="wizard"
method="GET">
> <xf:caption>Cocoon Deployment Information</xf:caption>
> <error>
> <xf:violations class="error"/>
> </error>
> <xf:textbox ref="/number">
> <xf:caption>Number of deployments</xf:caption>
> <xf:violations class="error"/>
> </xf:textbox>
> <xf:textbox ref="/liveUrl">
> <xf:caption>Live URL</xf:caption>
> <xf:help>You must enter a valid URL</xf:help>
> <xf:violations class="error"/>
> </xf:textbox>
> <xf:selectBoolean ref="/publish">
> <xf:caption>Publish</xf:caption>
> </xf:selectBoolean>
> <xf:group nodeset="" id="favorites_group">
> <xf:caption>Favorite web sites</xf:caption>
> <!--
>               repeat is a very powerful iterator tag,
>               because it iterates over a nodeset resulting from
>               the 'nodeset' selector attribute.
>               Very similar to xslt's for-each tag.
>
>               In this case we iterate over the top three favorite
>               web sites.
>             -->
> <xf:repeat nodeset="favorite[position() &lt;= 3]" id="favorites">
> <xf:textbox ref="." class="info">
> <xf:caption>URL:</xf:caption>
> </xf:textbox>
> </xf:repeat>
> </xf:group>
> <xf:submit id="prev" continuation="back" class="button">
> <xf:caption>Prev</xf:caption>
> <xf:hint>Go to previous page</xf:hint>
> </xf:submit>
> <xf:submit id="next" continuation="forward" class="button">
> <xf:caption>Next</xf:caption>
> <xf:hint>Go to next page</xf:hint>
> </xf:submit>
> </xf:form>
> <xf:output ref="count" id="show_count" form="form-feedback" class="info">
> <xf:caption>Visits Count</xf:caption>
> </xf:output>
> </document>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0" ?>
> <document>
>     <br/><br/><br/>
>     <table align="center" width="50%" cellspacing="20">
>         <tr>
>             <td align="center">
>                 <h1>
>                     Congratulations, Wizard Complete!
>                 </h1>
>             </td>
>         </tr>
>
>         <tr>
>             <td align="center" class="info">
>                 <code>
>                     Your feedback form was processed successfully.
>                 </code>
>             </td>
>         </tr>
>
>         <tr>
>             <td align="center">
>                 <h3>
>                     <a href="wizard">Go to home page.</a>
>                 </h3>
>             </td>
>         </tr>
>     </table>
> </document>
>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0" ?>
>
> <document>
>     <br/><br/><br/>
>     <table align="center" width="50%" cellspacing="20">
>         <tr>
>             <td align="center">
>                 <h1>
>                     Welcome !
>                 </h1>
>             </td>
>         </tr>
>
>         <tr>
>             <td align="center" class="info">
>                 <p>
>                 This wizard will collect feedback information
>                 for the
>                 <a href="http://xml.apache.org/cocoon/";>Apache Cocoon</a>
>                 project.
>                 </p>
>                 <p>
>                  See <a href="overview.html">overview</a> documentation.
>                 </p>
>             </td>
>         </tr>
>
>         <tr>
>             <td align="center">
>                 <h3>
>                     <a href="wizard?cocoon-action-start=true">
>                         Start!
>                     </a>
>                 </h3>
>             </td>
>         </tr>
>     </table>
> </document>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0"?>
> <!--
>
> XMLForm instance document for the Cocoon Feedback Wizard.
>
>   author: Torsten Curdt, [EMAIL PROTECTED], March 2002
>     author: Ivelin Ivanov, [EMAIL PROTECTED], April 2002
>     author: Simon Price <[EMAIL PROTECTED]>, September 2002
>
> -->
> <document xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";>
>   <xf:form id="form-feedback" view="system" action="wizard" method="GET">
>     <xf:caption>System Information</xf:caption>
>     <error>
>       <xf:violations class="error"/>
>     </error>
>     <xf:group ref="/system">
>       <xf:selectOne ref="os" selectUIType="radio">
>         <xf:caption>OS</xf:caption>
>         <xf:item id="unix">
>           <xf:caption>Unix/Linux</xf:caption>
>           <xf:value>Unix</xf:value>
>         </xf:item>
>         <xf:item id="mac">
>           <xf:caption>Mac OS/X</xf:caption>
>           <xf:value>Mac OS/X</xf:value>
>         </xf:item>
>         <xf:item id="win">
>           <xf:caption>Windows 95/98/NT/2000/XP</xf:caption>
>           <xf:value>Windows</xf:value>
>         </xf:item>
>         <xf:item id="other">
>           <xf:caption>Other</xf:caption>
>           <xf:value>Other</xf:value>
>         </xf:item>
>       </xf:selectOne>
>       <xf:selectOne ref="processor">
>         <xf:caption>Processor</xf:caption>
>         <xf:item>
>           <xf:caption>AMD/Athlon</xf:caption>
>           <xf:value>Athlon</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>AMD/Duron</xf:caption>
>           <xf:value>Duron</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Pentium Celeron</xf:caption>
>           <xf:value>Celeron</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Pentium III</xf:caption>
>           <xf:value>p3</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Pentium IV</xf:caption>
>           <xf:value>p4</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Other</xf:caption>
>           <xf:value>other</xf:value>
>         </xf:item>
>       </xf:selectOne>
>       <xf:textbox ref="@ram">
>         <xf:caption>RAM</xf:caption>
>         <xf:violations class="error"/>
>       </xf:textbox>
>       <xf:selectOne ref="servletEngine">
>         <xf:caption>Servlet Engine</xf:caption>
>         <xf:item>
>           <xf:caption>Tomcat</xf:caption>
>           <xf:value>Tomcat</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Jetty</xf:caption>
>           <xf:value>Jetty</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Resin</xf:caption>
>           <xf:value>Resin</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Weblogic</xf:caption>
>           <xf:value>weblogic</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>WebSphere</xf:caption>
>           <xf:value>WebSphere</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Other</xf:caption>
>           <xf:value>other</xf:value>
>         </xf:item>
>       </xf:selectOne>
>       <xf:selectOne ref="javaVersion">
>         <xf:caption>Java Version</xf:caption>
>         <xf:item>
>           <xf:caption>1.1</xf:caption>
>           <xf:value>1.1</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>1.2</xf:caption>
>           <xf:value>1.2</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>1.3</xf:caption>
>           <xf:value>1.3</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>1.4</xf:caption>
>           <xf:value>1.4</xf:value>
>         </xf:item>
>         <xf:item>
>           <xf:caption>Other</xf:caption>
>           <xf:value>Other</xf:value>
>         </xf:item>
>       </xf:selectOne>
>     </xf:group>
>     <xf:submit id="prev" continuation="back" class="button">
>       <xf:caption>Prev</xf:caption>
>       <xf:hint>Go to previous page</xf:hint>
>     </xf:submit>
>     <xf:submit id="next" continuation="forward" class="button">
>       <xf:caption>Next</xf:caption>
>       <xf:hint>Go to next page</xf:hint>
>     </xf:submit>
>   </xf:form>
>   <xf:output ref="count" id="show_count" form="form-feedback"
class="info">
>     <xf:caption>Visits Count</xf:caption>
>   </xf:output>
> </document>
>


----------------------------------------------------------------------------
----


> <?xml version="1.0"?>
> <!--
>
> XMLForm instance document for the Cocoon Feedback Wizard.
>
>     author: Ivelin Ivanov, [EMAIL PROTECTED], July 2002
>   author: Torsten Curdt, [EMAIL PROTECTED], March 2002
>     author: Simon Price <[EMAIL PROTECTED]>, September 2002
>
> -->
> <document xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";>
>   <xf:form id="form-feedback" view="userIdentity" action="wizard"
method="GET">
>     <xf:caption>Personal Information</xf:caption>
>     <error>
>       <xf:violations class="error"/>
>     </error>
>     <xf:textbox ref="/firstName">
>       <xf:caption>First Name</xf:caption>
>       <xf:violations class="error"/>
>     </xf:textbox>
>     <xf:textbox ref="/lastName">
>       <xf:caption>Last Name</xf:caption>
>       <xf:violations class="error"/>
>     </xf:textbox>
>     <xf:textbox ref="/email">
>       <xf:caption>Email</xf:caption>
>       <xf:help>Please check this carefully</xf:help>
>       <xf:violations class="error"/>
>     </xf:textbox>
>     <xf:textbox ref="/age">
>       <xf:caption>Age</xf:caption>
>       <xf:violations class="error"/>
>     </xf:textbox>
>     <xf:selectMany ref="/role" selectUIType="listbox">
>       <xf:caption>Professional roles</xf:caption>
>       <xf:help>Select one or more</xf:help>
>       <xf:item>
>         <xf:caption>Geek</xf:caption>
>         <xf:value>Geek</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Hacker</xf:caption>
>         <xf:value>Hacker</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Student</xf:caption>
>         <xf:value>Student</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>University Professor</xf:caption>
>         <xf:value>University Professor</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Software Developer</xf:caption>
>         <xf:value>Developer</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Technical Leader</xf:caption>
>         <xf:value>Tech Lead</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Development Manager</xf:caption>
>         <xf:value>Development Manager</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Executive</xf:caption>
>         <xf:value>Executive</xf:value>
>       </xf:item>
>       <xf:item>
>         <xf:caption>Heir of the Apache tribe</xf:caption>
>         <xf:value>Heir of the Apache tribe</xf:value>
>       </xf:item>
>     </xf:selectMany>
>     <xf:selectMany ref="/hobby" selectUIType="checkbox">
>       <xf:caption>Hobbies</xf:caption>
>       <xf:itemset nodeset="allHobbies">
>         <xf:caption ref="value"/>
>         <xf:value ref="key"/>
>       </xf:itemset>
>     </xf:selectMany>
>     <xf:textarea ref="notes" style="width:8cm; height:3cm">
>       <xf:caption>Additional Notes</xf:caption>
>     </xf:textarea>
>     <!-- hidden model attribute -->
>     <xf:hidden ref="hidden">
>       <xf:value>true</xf:value>
>     </xf:hidden>
>     <xf:submit continuation="foward" class="button">
>       <xf:caption>Next</xf:caption>
>       <xf:hint>Go to next page</xf:hint>
>     </xf:submit>
>   </xf:form>
>   <xf:output ref="count" id="show_count" form="form-feedback"
class="info">
>     <xf:caption>Visits Count</xf:caption>
>   </xf:output>
> </document>
>


----------------------------------------------------------------------------
----


> /*
>
>
============================================================================
>                    The Apache Software License, Version 1.1
>
============================================================================
>
>  Copyright (C) 1999-2003 The Apache Software Foundation. All rights
reserved.
>
>  Redistribution and use in source and binary forms, with or without
modifica-
>  tion, are permitted provided that the following conditions are met:
>
>  1. Redistributions of  source code must  retain the above copyright
notice,
>     this list of conditions and the following disclaimer.
>
>  2. Redistributions in binary form must reproduce the above copyright
notice,
>     this list of conditions and the following disclaimer in the
documentation
>     and/or other materials provided with the distribution.
>
>  3. The end-user documentation included with the redistribution, if any,
must
>     include  the following  acknowledgment:  "This product includes
software
>     developed  by the  Apache Software Foundation
(http://www.apache.org/)."
>     Alternately, this  acknowledgment may  appear in the software itself,
if
>     and wherever such third-party acknowledgments normally appear.
>
>  4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not
be
>     used to  endorse or promote  products derived from  this software
without
>     prior written permission. For written permission, please contact
>     [EMAIL PROTECTED]
>
>  5. Products  derived from this software may not  be called "Apache", nor
may
>     "Apache" appear  in their name,  without prior written permission  of
the
>     Apache Software Foundation.
>
>  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES,
>  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND
>  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL
THE
>  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY
DIRECT,
>  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
(INCLU-
>  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES;
LOSS
>  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND
ON
>  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR
TORT
>  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE
OF
>  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>
>  This software  consists of voluntary contributions made  by many
individuals
>  on  behalf of the Apache Software  Foundation and was  originally created
by
>  Stefano Mazzocchi  <[EMAIL PROTECTED]>. For more  information on the
Apache
>  Software Foundation, please see <http://www.apache.org/>.
>
> */
>
> package org.apache.cocoon.transformation;
>
> import java.io.IOException;
> import java.lang.reflect.Array;
> import java.util.Collection;
> import java.util.Iterator;
> import java.util.Map;
> import java.util.SortedSet;
> import java.util.Stack;
>
> import org.apache.avalon.framework.parameters.Parameters;
> import org.apache.cocoon.ProcessingException;
> import org.apache.cocoon.components.validation.Violation;
> import org.apache.cocoon.components.xmlform.Form;
> import org.apache.cocoon.environment.SourceResolver;
> import org.apache.cocoon.xml.dom.DOMStreamer;
> import org.w3c.dom.DocumentFragment;
> import org.xml.sax.Attributes;
> import org.xml.sax.SAXException;
> import org.xml.sax.helpers.AttributesImpl;
> import org.apache.cocoon.components.flow.WebContinuation;
> import org.apache.cocoon.environment.Environment;
> /**
>  * Transforms a document with XMLForm
>  * elements into a document in the same namespace,
>  * but with populated values for the XPath references
>  * to the form's model attributes
>  *
>  *
>  * @author Ivelin Ivanov <[EMAIL PROTECTED]>, June 2002
>  * @author Andrew Timberlake <[EMAIL PROTECTED]>, June 2002
>  * @author Michael Ratliff, [EMAIL PROTECTED]
<[EMAIL PROTECTED]>, May 2002
>  * @author Torsten Curdt <[EMAIL PROTECTED]>, March 2002
>  * @author Simon Price <[EMAIL PROTECTED]>, September 2002
>  *
>  */
>
> public class XMLFormTransformer extends AbstractSAXTransformer
> {
>
>   // @todo : implements CacheableProcessingComponent {
>
>
>
>   public final static String NS =
"http://xml.apache.org/cocoon/xmlform/2002";;
>   private final static String NS_PREFIX = "xf";
>   public final static Attributes NOATTR = new AttributesImpl();
>   private final static String XMLNS_PREFIX = "xmlns";
>
>   /**
>    * The main tag in the XMLForm namespace
>    * almost all other tags have to appear within the form tag
>    * The id attribute refers to a xmlform.Form object
>    * available in the current Request or Session
>    *
>    * &lt;form id="form-feedback">
>    *  &lt;output ref="user/age"/>
>    *  &lt;textbox ref="user/name"/>
>    * &lt;/form>
>    */
>   public final static String TAG_FORM = "form";
>   public final static String TAG_FORM_ATTR_ID = "id";
>   public final static String TAG_FORM_ATTR_VIEW = "view";
>
>   /**
>    * the only tag which can be used outside of the form tag
>    * with reference to the form id,
>    * &lt;output ref="user/age" id="form-feedback"/>
>    */
>   public final static String TAG_OUTPUT = "output";
>   public final static String TAG_OUTPUT_ATTR_FORM = TAG_FORM;
>
>   /**
>    * can be used directly under the form tag
>    * to enlist all field violations or
>    * within a field tag to enlist only the violations for the field.
>    * <br>
>    * <pre>
>    * &lt;form id="form-feedback">
>    *  &lt;violations/>
>    *  &lt;textbox ref="user/name">
>    *    &lt;violations/>
>    *  &lt;/textbox>
>    * &lt;/form>
>    * </pre>
>    *
>    * When used under the forms tag it is transformed to a set of:
>    * <br>
>    * &lt;violation ref="user/age">Age must be a positive number
&lt;/violation>
>    * <br>
>    * and when used within a field it is transformed to a set of:
>    * <br>
>    * &lt;violation>Age must be a positive number &lt;/violation>
>    * <br>
>    * The only difference is that the ref tag is used in the first case,
>    * while in the second it is omited.
>    *
>    */
>   public final static String TAG_INSERTVIOLATIONS = "violations";
>
>   /** the name of the elements which replace the violations tag */
>   public final static String TAG_VIOLATION = "violation";
>
>   /** action buttons */
>   public final static String TAG_SUBMIT = "submit";
>   public final static String TAG_CANCEL = "cancel";
>   public final static String TAG_RESET = "reset";
>
>   public final static String TAG_CAPTION = "caption";
>   public final static String TAG_HINT = "hint";
>   public final static String TAG_HELP = "help";
>   public final static String TAG_TEXTBOX = "textbox";
>   public final static String TAG_TEXTAREA = "textarea";
>   public final static String TAG_PASSWORD = "password";
>   public final static String TAG_SELECTBOOLEAN = "selectBoolean";
>   public final static String TAG_SELECTONE = "selectOne";
>   public final static String TAG_SELECTMANY = "selectMany";
>   public final static String TAG_ITEMSET = "itemset";
>   public final static String TAG_ITEM = "item";
>   public final static String TAG_VALUE = "value";
>   public final static String TAG_HIDDEN = "hidden";
>
>   /**
>    * grouping tag
>    *
>    * <pre>
>    *  <group ref="address">
>    *   <caption>Shipping Address</caption>
>    *     <input ref="line_1">
>    *       <caption>Address line 1</caption>
>    *     </input>
>    *     <input ref="line_2">
>    *       <caption>Address line 2</caption>
>    *     </input>
>    *     <input ref="postcode">
>    *       <caption>Postcode</caption>
>    *     </input>
>    * </group>
>    * </pre>
>    *
>    *
>    */
>   public final static String TAG_GROUP = "group";
>
>   /**
>    *  repeat tag
>    *
>    *  <repeat nodeset="/cart/items/item">
>    *    <input ref="." .../><html:br/>
>    *  </repeat>
>    *
>    *
>    */
>
>   public final static String TAG_REPEAT = "repeat";
>
>
>   /**
>    * this attribute is used within the
>    * <code>repeat</code> tag
>    * to represent an XPath node set selector from
>    * the underlying xmlform model.
>    */
>   public final static String TAG_REPEAT_ATTR_NODESET = "nodeset";
>
>   /**
>    * The current fully expanded reference
>    * in the form model.
>    *
>    */
>   private String cannonicalRef = null;
>
>   /**
>    * Tracks the current repeat tag depth,
>    * when there is one in scope
>    */
>   private int repeatTagDepth = -1;
>
>   /**
>    * The nodeset selector string of the
>    * currently processed repeat tag (if any)
>    */
>   private String nodeset = null;
>
>
>   /**
>    * The flag annotating if the transformer is
>    * working on a repeat tag
>    */
>   private boolean isRecording = false;
>
>
>   /**
>    * Flag to let us know if the transformer is working
>    * on a hidden tag
>    */
>   private boolean isHiddenTag = false;
>
>   /**
>    * Flag to let us know that the hidden element contains
>    * a value child element.
>    */
>   private boolean hasHiddenTagValue = false;
>
>
>   /**
>    * the ref value of the current field
>    * used by the violations tag
>    */
>   private Stack refStack = null;
>
>   /**
>    * Tracks the current depth of the XML tree
>    */
>   private int currentTagDepth = 0;
>
>
>   /**
>    * this attribute is used within all field tags
>    * to represent an XPath reference to the attribute of
>    * the underlying model.
>    */
>   public final static String TAG_COMMON_ATTR_REF = "ref";
>
>
>   /** the stack of nested forms.
>    * Although nested form tags are not allowed, it is possible
>    * that an output tag (with reference to another form) might be nested
within a form tag.
>    * In this case elements under the output tag (like caption) can
reference properties
>    * of the form of the enclosing output tag.
>    */
>
>   private Stack formStack = null;
>
>
>   /**
>    * Since form elements cannot be nested,
>    * at most one possible value for the current form view is available.
>    */
>   private String currentFormView = null;
>
>
>   private Object value_;
>
>
>
>     /**
>      * Setup the next round.
>      * The instance variables are initialised.
>      * @param resolver The current SourceResolver
>      * @param objectModel The objectModel of the environment.
>      * @param src The value of the src attribute in the sitemap.
>      * @param par The parameters from the sitemap.
>      */
>   public void setup(
>                       SourceResolver resolver,
>                       Map                   objectModel,
>                       String                 src,
>                       Parameters        par)
>     throws ProcessingException,
>            SAXException,
>            IOException
>     {
>       super.setup( resolver, objectModel, src, par );
>       if (request == null)
>       {
>         getLogger().debug("no request object");
>         throw new ProcessingException("no request object");
>       }
>
>       // set the XMLForm namespace as the one
>       // this transformer is interested to work on
>       namespaceURI = NS;
>
>       // init tracking parameters
>       formStack = new Stack();
>       cannonicalRef = "";
>       refStack = new Stack();
>       currentTagDepth = 0;
>       repeatTagDepth = -1;
>       isRecording = false;
>       nodeset = null;
>   }
>
>
>     /**
>      *  Recycle this component.
>      */
>     public void recycle()
>     {
>       // init tracking parameters
>       formStack.clear();
>       formStack = null;
>       cannonicalRef = null;
>       refStack.clear();
>       refStack = null;
>       currentTagDepth = 0;
>       repeatTagDepth = -1;
>       isRecording = false;
>       nodeset = null;
>
>       super.recycle();
>     }
>
>
>     /**
>      * Start processing elements of our namespace.
>      * This hook is invoked for each sax event with our namespace.
>      * @param uri The namespace of the element.
>      * @param name The local name of the element.
>      * @param raw The qualified name of the element.
>      * @param attr The attributes of the element.
>      */
>     public void startTransformingElement(String uri,
>                                          String name,
>                                          String raw,
>                                          Attributes attributes )
>     throws ProcessingException, IOException, SAXException
>     {
>       try
>       {
>         // avoid endless loop for elements in our namespace
>         // when outputting the elements themselves
>         this.ignoreHooksCount = 1;
>
>         if (this.getLogger().isDebugEnabled() == true)
>         {
>             this.getLogger().debug("BEGIN startTransformingElement uri=" +
uri + ", name=" + name + ", raw=" + raw + ", attr=" + attributes + ")");
>         }
>
>       // top level element in our namespace
>       // set an xmlns:xf="XMLForm namespace..." attribute
>       // to explicitely define the prefix to namespace binding
>       if (currentTagDepth == 0)
>       {
>         AttributesImpl atts;
>         if (attributes == null || attributes.getLength() == 0) {
>             atts = new AttributesImpl();
>         } else {
>             atts = new AttributesImpl(attributes);
>         }
>         atts.addAttribute( null, NS_PREFIX, XMLNS_PREFIX + ":" +
NS_PREFIX, "CDATA", NS);
>         attributes = atts;
>       }
>
>       // track the tree depth
>       ++currentTagDepth;
>
>       // if within a repeat tag, keep recording
>       // when recording, nothing is actively processed
>       if (isRecording)
>       {
>         // just record the SAX event
>         super.startElement( uri, name, raw, attributes);
>       }
>      // when a new repeat tag is discovered
>      // start recording
>      // the repeat will be unrolled after the repeat tag ends
>      else if (TAG_REPEAT.equals( name ))
>      {
>        repeatTagDepth = currentTagDepth;
>        isRecording = true;
>
>       // get the nodeset selector string
>       nodeset = attributes.getValue(TAG_REPEAT_ATTR_NODESET);
>
>       if (nodeset == null)
>       {
>          throw new SAXException( name + " element should provide a '" +
TAG_REPEAT_ATTR_NODESET + "' attribute" );
>       }
>
>        // open the repeat tag in the output document
>        super.startElement( uri, name, raw, attributes);
>        // and start recording its content
>        startRecording();
>      }
>      // when a new itemset tag (used within select) is discovered
>      // start recording
>      // the itemset will be unrolled after the tag ends
>      // The difference with the repeat tag is that itemset is
>      // unrolled in multiple item tags.
>      else if ( TAG_ITEMSET.equals( name ) )
>      {
>        repeatTagDepth = currentTagDepth;
>        isRecording = true;
>
>       // get the nodeset selector string
>       nodeset = attributes.getValue(TAG_REPEAT_ATTR_NODESET);
>
>       if (nodeset == null)
>       {
>          throw new SAXException( name + " element should provide a '" +
TAG_REPEAT_ATTR_NODESET + "' attribute" );
>       }
>
>        // start recording its content
>        startRecording();
>      }
>     else // if not a repeat tag
>     {
>         // if this tag has a "ref" attribute, then
>         // add its value to the refStack
>         String aref = attributes.getValue( TAG_COMMON_ATTR_REF );
>         if ( aref != null )
>         {
>           // put on top of the ref stack the full ref
>           // append the new ref to the last stack top if not referencing
the root
>           if ( !refStack.isEmpty() )
>           {
>           // this is a nested reference
>         cannonicalRef = aref.startsWith ("/") ? aref : ( ((Entry)
refStack.peek()).getValue() + "/" + aref );
>           }
>           else
>           {
>             // top level reference
>             cannonicalRef = aref;
>           }
>           Entry entry = new Entry( new Integer(currentTagDepth),
cannonicalRef);
>           refStack.push( entry );
>
>           // replace the ref attribute's value(path) with its full
cannonical form
>           AttributesImpl atts = new AttributesImpl( attributes );
>           int refIdx = atts.getIndex ( TAG_COMMON_ATTR_REF );
>           atts.setValue ( refIdx, cannonicalRef );
>           attributes = atts;
>         }
>
>         // match tag name and apply transformation logic
>         if (TAG_FORM.equals(name))
>         {
>           startElementForm( uri, name, raw, attributes );
>         }
>         else if ( TAG_OUTPUT.equals(name) )
>         {
>           startElementOutput( uri, name, raw, attributes );
>         } // end if TAG_OUTPUT
>         else if (TAG_INSERTVIOLATIONS.equals(name))
>           {
>             startElementViolations( uri, name, raw, attributes );
>           } // end if TAG_INSERTVIOLATIONS
>
>         // if we're within a xf:hidden element
>         // and a value sub-element has been provided
>         // in the markup, then it will be left
>         // unchanged. Otherwise we will
>         // render the value of the referenced model
>         // attribute
>         else if ( isHiddenTag && TAG_VALUE.equals( name ) )
>         {
>     hasHiddenTagValue = true;
>           super.startElement( uri, name, raw, attributes);
>         }
>
>         // if we are not within an enclosing form
>         // then we can't process the following nested tags
>         else if (!formStack.isEmpty())
>         {
>
>           if (
>                     TAG_TEXTBOX.equals(name) ||
>                     TAG_TEXTAREA.equals(name) ||
>                     TAG_PASSWORD.equals(name) ||
>                     TAG_SELECTBOOLEAN.equals(name) ||
>                     TAG_SELECTONE.equals(name) ||
>                     TAG_SELECTMANY.equals(name)
>              )
>           {
>             startElementInputField( uri, name, raw, attributes );
>           }
>           else if (
>                     TAG_CAPTION.equals(name) ||
>                     TAG_HINT.equals(name) ||
>                     TAG_HELP.equals(name) ||
>                     TAG_VALUE.equals(name)
>                    )
>          {
>            startElementWithOptionalRefAndSimpleContent( uri, name, raw,
attributes );
>          }
>           else if (
>    TAG_SUBMIT.equals(name) )
>   {
>       String continuation = attributes.getValue("continuation");
>       if (continuation != null) {
>   WebContinuation kont
>       = (WebContinuation)((Environment)resolver).getAttribute("kont");
>   String id;
>   int level = 0;
>   if (continuation.equals("back")) {
>       level = 3;
>   }
>   id = kont.getContinuation(level).getId();
>   AttributesImpl impl = new AttributesImpl(attributes);
>   int index = impl.getIndex("id");
>   if (index >= 0) {
>       impl.setValue(index, id);
>   } else {
>       impl.addAttribute("", "id", "id", "", id);
>   }
>   attributes = impl;
>       }
>       super.startElement(uri, name, raw, attributes);
>   }
>           else if (
>             TAG_CANCEL.equals(name) ||
>             TAG_RESET.equals(name) ||
>             TAG_ITEM.equals(name)
>             )
>           {
>             super.startElement(uri, name, raw, attributes);
>           }
>           else if (TAG_HIDDEN.equals(name))
>           {
>             // raise the flag that we're within a hidden element
>             // since there are intricacies in
>             // handling the value sub-element
>       isHiddenTag = true;
>       startElementInputField( uri, name, raw, attributes );
>           }
>           else
>           {
>               getLogger().error("pass through element [" +
String.valueOf(name) + "]");
>               super.startElement(uri, name, raw, attributes);
>           }
>         }
>       } // end else (not a repeat tag)
>     }
>     finally
>     {
>       // reset ignore counter
>       this.ignoreHooksCount = 0;
>     }
>
>
>     if (this.getLogger().isDebugEnabled() == true)
>     {
>           this.getLogger().debug("END startTransformingElement");
>     }
>   } // end of startTransformingElement
>
>
>   protected void startElementForm(String uri, String name, String raw,
Attributes attributes) throws SAXException
>   {
>     String id = attributes.getValue(TAG_FORM_ATTR_ID);
>
>     // currently form elements cannot be nested
>     if ( !formStack.isEmpty() )
>     {
>       String error = "Form nodes should not be nested ! Current form [id="
+ formStack.peek() + "], nested form [id=" + String.valueOf(id) + "]";
>       getLogger().error( error );
>       throw new IllegalStateException( error );
>     }
>
>     super.startElement(uri, name, raw, attributes);
>
>     // load up the referenced form
>     Form currentForm =  Form.lookup( objectModel, id );
>
>     // if the form wasn't found, we're in trouble
>     if (currentForm == null)
>     {
>       String error = "Form is null [id=" + String.valueOf(id) + "]";
>       getLogger().error( error );
>       throw new IllegalStateException( error );
>     };
>
>
>     formStack.push( currentForm );
>
>     // memorize the current form view
>     // it will be needed when saving expected references to properties
>     currentFormView = attributes.getValue(TAG_FORM_ATTR_VIEW);
>
>     // clear previously saved form state for this view
>     resetSavedModelReferences();
>
>   } // end of startElementForm
>
>
>
>   protected void startElementViolations(String uri, String name, String
raw, Attributes attributes) throws SAXException
>   {
>
>       // we will either use the locally referenced form id
>       // or the global id. At least one of the two must be available
>       Form form = null;
>       String formAttr = attributes.getValue( TAG_OUTPUT_ATTR_FORM );
>       if (formAttr == null)
>       {
>         if ( formStack.isEmpty() )
>         {
>           throw new SAXException( "When used outside of a form tag, the
output tag requires an '" + TAG_OUTPUT_ATTR_FORM + "' attribute" );
>         }
>         form = (Form) formStack.peek();
>       }
>       else
>       {
>         form = Form.lookup( objectModel, formAttr );
>       }
>
>       SortedSet violations = form.getViolationsAsSortedSet();
>
>       // if there are no violations, there is nothing to show
>       if (violations == null)  return;
>
>
>       // if we're immediately under the form tag
>       // and parent "ref" attribute is not available
>       if ( refStack.isEmpty () )
>       {
>         for (Iterator it = violations.iterator(); it.hasNext();)
>         {
>           Violation violation = (Violation) it.next();
>
>           // render <violation> tag
>
>           // set the ref attribute
>           AttributesImpl atts;
>           if (attributes == null || attributes.getLength() == 0) {
>               atts = new AttributesImpl();
>           } else {
>               atts = new AttributesImpl(attributes);
>           }
>           // atts.addAttribute( NS, TAG_COMMON_ATTR_REF, NS_PREFIX + ":" +
TAG_COMMON_ATTR_REF, "CDATA", violation.getPath());
>           atts.addAttribute( null, TAG_COMMON_ATTR_REF,
TAG_COMMON_ATTR_REF, "CDATA", violation.getPath());
>
>           // now start the element
>           super.startElement(uri, TAG_VIOLATION, NS_PREFIX + ":" +
TAG_VIOLATION, atts);
>
>           // set message
>           String vm = violation.getMessage();
>           super.characters( vm.toCharArray(), 0, vm.length());
>
>           super.endElement(uri, TAG_VIOLATION, NS_PREFIX + ":" +
TAG_VIOLATION);
>         }
>       } // end if (currentRef_ == null)
>       else
>       {
>         Entry entry = (Entry) refStack.peek ();
>         String currentRef = (String) entry.getValue ();
>         Violation v = new Violation();
>         v.setPath( currentRef );
>         Collection restViolations = violations.tailSet ( v );
>         Iterator rviter = restViolations.iterator ();
>         while ( rviter.hasNext () )
>         {
>           Violation nextViolation = (Violation) rviter.next ();
>           // we're only interested in violations
>           // with matching reference
>           if ( !currentRef.equals (nextViolation.getPath () ) ) break;
>
>           // render <violation> tag
>           super.startElement(uri, TAG_VIOLATION, NS_PREFIX + ":" +
TAG_VIOLATION, attributes );
>           // set message
>           String vm = nextViolation.getMessage();
>           super.characters( vm.toCharArray(), 0, vm.length());
>           super.endElement(uri, TAG_VIOLATION, NS_PREFIX + ":" +
TAG_VIOLATION);
>         }
>       }
>   } // end of startElementViolations
>
>
>
>   /**
>    * Since the ouput tag is the only one which can be used
>    * outside of a form tag, it needs some special treatment
>    *
>    */
>   protected void startElementOutput(String uri, String name, String raw,
Attributes attributes)
>     throws SAXException
>   {
>
>         // we will either use the locally referenced form id
>         // or the global id. At least one of the two must be available
>         Form form = null;
>         String formAttr = attributes.getValue( TAG_OUTPUT_ATTR_FORM );
>         if (formAttr == null)
>         {
>           if ( formStack.isEmpty() )
>           {
>             throw new SAXException( "When used outside of a form tag, the
output tag requires an '" + TAG_OUTPUT_ATTR_FORM + "' attribute" );
>           }
>           form = (Form) formStack.peek();
>         }
>         else
>         {
>           form = Form.lookup( objectModel, formAttr );
>         }
>         formStack.push( form );
>
>         startElementSimpleField( uri, name, raw, attributes );
>
>   } // end of startElementOutput
>
>
>   /**
>    *
>    * Renders elements, which are used for input
>    *
>    * TAG_TEXTBOX, TAG_TEXTAREA, TAG_PASSWORD, TAG_SELECTBOOLEAN,
>    * TAG_SELECTONE, TAG_SELECTMANY
>    *
>    */
>   protected void startElementInputField(String uri, String name, String
raw, Attributes attributes )
>     throws SAXException
>   {
>     startElementSimpleField( uri, name, raw, attributes );
>
>     String ref = attributes.getValue(TAG_COMMON_ATTR_REF);
>     if (ref == null)
>     {
>        throw new SAXException( name + " element should provide a '" +
TAG_COMMON_ATTR_REF + "' attribute" );
>     }
>     saveModelReferenceForFormView( ref, name );
>   }
>
>
>   protected void startElementSimpleField(String uri, String name, String
raw, Attributes attributes )
>     throws SAXException
>   {
>       String ref = attributes.getValue(TAG_COMMON_ATTR_REF);
>
>       if (ref == null)
>       {
>          throw new SAXException( name + " element should provide a '" +
TAG_COMMON_ATTR_REF + "' attribute" );
>       }
>
>       if ( formStack.isEmpty() )
>       {
>          throw new SAXException( name + " element should be either nested
within a form tag or provide a form attribute" );
>       }
>
>       Form form = getCurrentForm();
>
>       getLogger().debug("[" + String.valueOf( name ) + "] getting value
from form [id=" + form.getId() + ", ref=" + String.valueOf(ref) + "]");
>
>       // retrieve current value of referenced property
>       value_ = form.getValue( ref );
>
>       // we will only forward the SAX event once we know
>       // that the value of the tag is available
>       super.startElement(uri, name, raw, attributes);
>
>       getLogger().debug("Value of form [id=" + form.getId() + ", ref=" +
String.valueOf(ref) + "] = [" + value_ + "]") ;
>
>     // Only render value sub-elements
>       // at this point
>       // if this is not a xf:hidden element.
>     if( !isHiddenTag ) renderValueSubElements();
>      } // end of startElementSimpleField
>
>
>   /**
>    * Let the form wrapper know that this reference should be expected
>    * when data is submitted by the client for the current form view.
>    * The name of the XML tag is also saved to help the form populator
>    * find an appropriate default value when one is not provided in the
http request.
>    */
>   protected void saveModelReferenceForFormView( String ref, String name )
>   {
>     // the xf:form/@view attribute is not mandatory
>     // although it is strongly recommended
>     if (currentFormView != null)
>       {
>       Form form = getCurrentForm();
>       form.saveExpectedModelReferenceForView( currentFormView, ref,
name );
>       }
>   }
>
>   /**
>    * When the transformer starts rendering a new form element
>    * It needs to reset previously saved references for another
>    * transformation of the same view.
>    */
>   protected void resetSavedModelReferences()
>   {
>     if ( currentFormView != null )
>     {
>       Form form = getCurrentForm();
>       form.clearSavedModelReferences( currentFormView );
>     }
>
>   }
>
>   /**
>    * Used for elements which are not two directional.
>    * They are displayed but cannot be used for submitting new values
>    *
>    * TAG_CAPTION, TAG_HINT, TAG_HELP, TAG_VALUE
>    *
>    */
>   protected void startElementWithOptionalRefAndSimpleContent(String uri,
String name, String raw, Attributes attributes )
>     throws SAXException
>   {
>       String ref = attributes.getValue(TAG_COMMON_ATTR_REF);
>
>       if (ref == null) // ref attribute is not provided
>       {
>          super.startElement( uri, name, raw, attributes );
>          return;
>       }
>
>       if ( formStack.isEmpty() )
>       {
>          throw new SAXException( name + " element should be either nested
within a form tag or provide a form attribute" );
>       }
>
>       Form form = (Form) formStack.peek();
>
>       getLogger().debug("[" + String.valueOf( name ) + "] getting value
from form [id=" + form.getId() + ", ref=" + String.valueOf(ref) + "]");
>
>       Object value = form.getValue( ref );
>
>       // we will only forward the SAX event once we know
>       // that the value of the tag is available
>       super.startElement(uri, name, raw, attributes);
>
>       getLogger().debug("Value of form [id=" + form.getId() + ", ref=" +
String.valueOf(ref) + "] = [" + value_ + "]") ;
>
>       // Now render the character data inside the tag
>       String v = String.valueOf( value );
>       super.characters(v.toCharArray(),0,v.length());
>
>   } // end of startElementSimpleFieldWithOptionalRef
>
>
> /**
> * Renders one or more xf:value elements
>      * depending on whether _value is a
> * collection, array or not.
> *
> * @throws SAXException
> */
> private void renderValueSubElements() throws SAXException
> {
> // render the value subelement(s)
> if (value_ instanceof Collection)
> {
>   Iterator i=((Collection) value_).iterator();
>   while (i.hasNext())
>   {
> renderValueSubElement( i.next() );
>   }
> }
> else if ( value_ != null && value_.getClass().isArray () )
> {
>   int len = Array.getLength ( value_ );
>   for (int i = 0; i < len; i++ )
>   {
> renderValueSubElement( Array.get ( value_, i ) );
>   }
> }
> else
> {
>   renderValueSubElement( value_ );
> }
> }
>
>
>
>
>   /**
>    * Outputs a <xf:value> element.
>    * Used when transforming XMLForm elements
>    * with reference to the model
>    *
>    * @param vobj provides the text content
>    * within the <xf:value> element
>    *
>    */
>   protected void renderValueSubElement( Object vobj )
>     throws SAXException
>   {
>     super.startElement( NS, "value", NS_PREFIX + ":" + "value", NOATTR);
>       if (vobj != null)
>       {
>         String v = String.valueOf( vobj );
>         super.characters(v.toCharArray(),0,v.length());
>       }
>       super.endElement( NS, "value", NS_PREFIX + ":" + "value" );
>   }
>
>     /**
>      * Start processing elements of our namespace.
>      * This hook is invoked for each sax event with our namespace.
>      * @param uri The namespace of the element.
>      * @param name The local name of the element.
>      * @param raw The qualified name of the element.
>      */
>     public void endTransformingElement(
>          String uri,
>          String name,
>          String raw)
>     throws ProcessingException, IOException, SAXException
>   {
>     if (this.getLogger().isDebugEnabled() == true)
>     {
>         this.getLogger().debug("BEGIN endTransformingElement uri=" + uri +
", name=" + name + ", raw=" + raw + ")");
>     }
>
>
>     try
>     {
>         // avoid endless loop for elements in our namespace
>         this.ignoreHooksCount = 1;
>
>
>      // when the end of an active repeat tag is reached
>      // stop recording, unroll the repeat tag content
>      // for each node in the node set,
>      // then close the repeat tag
>      if ( (TAG_REPEAT.equals( name ) )
>           && (repeatTagDepth == currentTagDepth))
>      {
>        isRecording = false;
>        DocumentFragment docFragment = endRecording();
>        unrollRepeatTag( docFragment );
>        nodeset = null;
>        // close the repeat tag
>        super.endElement(uri, name, raw);
>      }
>      // similarly for an itemset tag
>      else if ( (TAG_ITEMSET.equals( name ))
>           && (repeatTagDepth == currentTagDepth))
>      {
>        isRecording = false;
>        DocumentFragment docFragment = endRecording();
>        unrollItemSetTag( docFragment );
>        nodeset = null;
>      }
>       // if within a repeat tag, keep recording
>       // when recording, nothing is actively processed
>      else  if (isRecording)
>      {
>         // just record the SAX event
>         super.endElement(uri, name, raw);
>      }
>     else // if not a repeat tag
>     {
>
>         // keep the ref stack in synch with the tree navigation
>         if ( !refStack.isEmpty () )
>         {
>           Entry entry = (Entry) refStack.peek();
>           Integer refDepth = (Integer) entry.getKey ();
>           if ( currentTagDepth <= refDepth.intValue () )
>           {
>             refStack.pop();
>             cannonicalRef = refStack.isEmpty () ? "" : (String)( (Entry)
(refStack.peek ()) ).getValue();
>           }
>         }
>
>
>         if (TAG_INSERTVIOLATIONS.equals(name))
>         {
>           // all violations were rendered completely in the startElement
method
>         }
>         else if (TAG_FORM.equals(name))
>         {
>           // pop currentForm from stack since we're getting out of its
scope
>           formStack.pop();
>           super.endElement(uri, name, raw);
>         }
>         else if (
>                 TAG_TEXTBOX.equals(name) ||
>                 TAG_TEXTAREA.equals(name) ||
>                 TAG_PASSWORD.equals(name) ||
>                 TAG_SELECTBOOLEAN.equals(name) ||
>                 TAG_SELECTONE.equals(name) ||
>                 TAG_SELECTMANY.equals(name) ||
>                 TAG_SUBMIT.equals(name) ||
>                 TAG_CAPTION.equals( name ) ||
>                 TAG_VALUE.equals( name ) ||
>                 TAG_HINT.equals( name ) ||
>                 TAG_HELP.equals( name )
>                 )
>         {
>           super.endElement(uri, name, raw);
>         }
>         else if ( TAG_OUTPUT.equals(name) )
>         {
>           formStack.pop();
>           super.endElement(uri, name, raw);
>         }
>
>         else if (TAG_HIDDEN.equals(name))
>         {
>     isHiddenTag = false;
>     hasHiddenTagValue = false;
>           // if value sub-element was not
>           // provided in the markup
>           // then render the value of the referenced
>           // model attribute, like normally done
>           // for other elements
>           if(! hasHiddenTagValue)
>     {
>     renderValueSubElements();
>     }
>             super.endElement(uri, name, raw);
>          }
>         else
>         {
>           getLogger().error("unknown element [" + String.valueOf(name) +
"]");
>           super.endElement(uri, name, raw);
>         }
>       } // else (not in a recording tag)
>     }
>     finally
>     {
>       // reset ignore hooks counter
>       this.ignoreHooksCount = 0;
>
>       // track the tree depth
>       --currentTagDepth;
>     }
>
>     if (this.getLogger().isDebugEnabled() == true)
>     {
>       this.getLogger().debug("END endTransformingElement");
>     }
>
>   } // end of endTransformingElement
>
>
>   /**
>    * Unroll the repeat tag.
>    * For each node in the repeat tag's nodeset selector result,
>    * render a <code>group</code> tag with a <code>ref</code>
>    * attribute which points to the location of the current node
>    * in the nodeset. Within each <code>group</code> tag,
>    * output the content of the repeat tag,
>    * by resolving all form model references within nested xmlform tags,
>    * relative to the <code>ref</code> attribute of the <code>group</code>
element.
>    *
>    * @param docFragment the content of the repeat tag
>    * @param nodeset the nodeset selector string
>    */
>   protected void unrollRepeatTag( DocumentFragment docFragment )
>     throws SAXException
>   {
>     int oldIgnoreHooksCount = ignoreHooksCount;
>     try
>     {
>       // reset ignore hooks counter
>       this.ignoreHooksCount = 0;
>       Form currentForm = (Form) formStack.peek();
>       Collection locations = currentForm.locate( nodeset );
>       Iterator iter = locations.iterator();
>       // iterate over each node in the nodeset
>       while ( iter.hasNext() )
>       {
>         String nextNodeLocation = (String) iter.next ();
>
>         // set the ref attribute to point to the current node
>         AttributesImpl atts = new AttributesImpl();
>         atts.addAttribute( null, TAG_COMMON_ATTR_REF, TAG_COMMON_ATTR_REF,
"CDATA", nextNodeLocation);
>
>         super.startElement(NS, TAG_GROUP, NS_PREFIX + ":" + TAG_GROUP,
atts);
>         if (value_ != null)
>         {
>           // stream back the recorder repeat content
>           DOMStreamer streamer = new DOMStreamer( this, this);
>           streamer.stream( docFragment );
>         }
>
>         super.endElement( NS, TAG_GROUP, NS_PREFIX + ":" + TAG_GROUP );
>
>       }
>     }
>     finally
>     {
>       ignoreHooksCount = oldIgnoreHooksCount;
>     }
>   } // unrollRepeatTag
>
>
>
>
>   /**
>    * Unroll the itemset tag.
>    * For each node in the itemset tag's nodeset selector result,
>    * render a <code>item</code> tag with a <code>ref</code>
>    * attribute which points to the location of the current node
>    * in the nodeset.
>    * Within each <code>item</code> tag,
>    * output the content of the itemset tag,
>    * by resolving all model references within nested caption and value
tags,
>    * relative to the <code>ref</code> attribute of the <code>item</code>
element.
>    *
>    * @param docFragment the content of the repeat tag
>    * @param nodeset the nodeset selector string
>    */
>   protected void unrollItemSetTag( DocumentFragment docFragment )
>     throws SAXException
>   {
>     int oldIgnoreHooksCount = ignoreHooksCount;
>     try
>     {
>       // reset ignore hooks counter
>       this.ignoreHooksCount = 0;
>
>       Form currentForm = (Form) formStack.peek();
>
>       Collection locations = currentForm.locate( nodeset );
>       Iterator iter = locations.iterator();
>       // iterate over each node in the nodeset
>       while ( iter.hasNext() )
>       {
>         String nextNodeLocation = (String) iter.next ();
>
>         // set the ref attribute to point to the current node
>         AttributesImpl atts = new AttributesImpl();
>         atts.addAttribute( null, TAG_COMMON_ATTR_REF, TAG_COMMON_ATTR_REF,
"CDATA", nextNodeLocation);
>
>         super.startElement(NS, TAG_ITEM, NS_PREFIX + ":" + TAG_ITEM,
atts);
>         if (value_ != null)
>         {
>           // stream back the recorder repeat content
>           DOMStreamer streamer = new DOMStreamer( this, this);
>           streamer.stream( docFragment );
>         }
>
>         super.endElement( NS, TAG_ITEM, NS_PREFIX + ":" + TAG_ITEM );
>
>       }
>     }
>     finally
>     {
>       ignoreHooksCount = oldIgnoreHooksCount;
>     }
>   } // unrollItemSetTag
>
>
>   protected Form getCurrentForm()
>   {
>     return (Form) formStack.peek();
>   }
>
>
>     /**
>      * refStack entry.
>      */
>   private static class Entry implements Map.Entry {
>     Object key;
>     Object value;
>
>     Entry(Object key, Object value) {
>         this.key = key;
>         this.value = value;
>     }
>
>     // Map.Entry Ops
>
>     public Object getKey() {
>         return key;
>     }
>
>     public Object getValue() {
>         return value;
>     }
>
>     public Object setValue(Object value) {
>         Object oldValue = this.value;
>         this.value = value;
>         return oldValue;
>     }
>
>     public boolean equals(Object o) {
>         if (!(o instanceof Map.Entry))
>         return false;
>         Map.Entry e = (Map.Entry)o;
>
>         return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
>            (value==null ? e.getValue()==null :
value.equals(e.getValue()));
>     }
>
>     public int hashCode() {
>         return getKey().hashCode () ^ (value==null ? 0 :
value.hashCode());
>     }
>
>     public String toString() {
>         return key+"="+value;
>     }
> }
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, email: [EMAIL PROTECTED]


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, email: [EMAIL PROTECTED]

Reply via email to