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>

Attachment: xmlform.js
Description: JavaScript source

<?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>

Attachment: feedbackWizard.js
Description: JavaScript source

/*

 ============================================================================
                   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]

Reply via email to