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() <= 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() <= 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
> *
> * <form id="form-feedback">
> * <output ref="user/age"/>
> * <textbox ref="user/name"/>
> * </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,
> * <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>
> * <form id="form-feedback">
> * <violations/>
> * <textbox ref="user/name">
> * <violations/>
> * </textbox>
> * </form>
> * </pre>
> *
> * When used under the forms tag it is transformed to a set of:
> * <br>
> * <violation ref="user/age">Age must be a positive number
</violation>
> * <br>
> * and when used within a field it is transformed to a set of:
> * <br>
> * <violation>Age must be a positive number </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]