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]