ivelin      02/04/17 10:59:16

  Modified:    src/scratchpad/src/org/apache/cocoon/components/modules/database
                        IfxSerialAutoIncrementModule.java
               src/scratchpad/src/org/apache/cocoon/validation
                        Violation.java
               src/scratchpad/src/org/apache/cocoon/validation/schematron
                        SchematronValidator.java
               src/scratchpad/src/org/apache/cocoon/xmlform Form.java
               src/scratchpad/webapp/mount/xmlform README.txt sitemap.xmap
  Added:       src/scratchpad/src/org/apache/cocoon/acting
                        AbstractXMLFormAction.java
               src/scratchpad/src/org/apache/cocoon/samples/xmlform
                        UserBean.java WizardAction.java
               src/scratchpad/src/org/apache/cocoon/transformation
                        XMLFormTransformer.java
               src/scratchpad/webapp/mount/xmlform/schematron
                        wizard-xmlform-sch-report.xml
               src/scratchpad/webapp/mount/xmlform/stylesheets
                        wizard2html.xsl xmlform2html.xsl
  Removed:     src/scratchpad/src/org/apache/cocoon/samples/xmlform
                        FormBinderAction.java NestedBean.java TestBean.java
                        ValidatingFormAction.java
               src/scratchpad/src/org/apache/cocoon/xmlform
                        FormBeanBinder.java
               src/scratchpad/webapp/mount/xmlform formbean2html-Demo2.xsl
                        formbean2html.xsl insertFormBean-Demo2.xml
                        insertFormBean.xml success-page-Demo2.html
               src/scratchpad/webapp/mount/xmlform/castor-mappings
                        test-mapping.xml
               src/scratchpad/webapp/mount/xmlform/schematron
                        schematron1-5.xsd some-xmlform.xml
                        xmlform-extensions.xsl xmlform-sch-report-Demo2.xml
                        xmlform-sch-report.xml xmlform-sch-report.xsl
                        xmlform-schematron.xsl xmlform-wrappers.xsl
                        xmlform.bat xslt.bat
  Log:
  XMLForm 0.9
  
  - Major Refactoring.
  - Proposed as base for Cocoon 2.1
  - Introduces an XForms (like) layer of Form content abstraction
  - Removed previous examples and demos to focus users on one usage pattern.
  - Introduced AbstractXMLFormAction which works together with Form and 
XMLFormTransformer
  
  Revision  Changes    Path
  1.1                  
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/acting/AbstractXMLFormAction.java
  
  Index: AbstractXMLFormAction.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/acting/AbstractXMLFormAction.java,v
 1.1 2002/04/17 17:59:15 ivelin Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/17 17:59:15 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   * 
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, 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 acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", 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 names without prior written
   *    permission of the Apache Group.
   *
   * 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 (INCLUDING, 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 based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.cocoon.acting;
  
  
  // Java classes
  import java.util.Map;
  import java.util.HashMap;
  import java.util.SortedSet;
  import java.util.Iterator;
  import java.util.Properties;
  import java.util.Enumeration;
  import java.io.InputStream;
  import java.io.FileInputStream;
  import java.io.File;
  
  // XML classes
  import javax.xml.transform.stream.StreamSource;
  import javax.xml.transform.TransformerException;
  import org.xml.sax.InputSource;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  // Framework classes
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.excalibur.pool.Poolable;
  
  // Cocoon classes
  import org.apache.cocoon.Constants;
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.environment.Source;
  import org.apache.cocoon.acting.*;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.ObjectModelHelper;
  import org.apache.cocoon.environment.Session;
  import org.apache.cocoon.environment.Context;
  
  // Schematron classes
  import org.apache.cocoon.validation.SchemaFactory;
  import org.apache.cocoon.validation.Schema;
  import org.apache.cocoon.validation.Validator;
  import org.apache.cocoon.validation.Violation;
  
  // Cocoon Form
  import org.apache.cocoon.xmlform.Form;
  
  
  
  /**
   *
   * This is the base action class for 
   * xmlform handling
   *
   * This action is Poolable which means that 
   * subclasses of this class should not be
   * concerned about thread safety.
   * The framework ensures that only one thread
   * has access to a concrete instance at any time.
   *
   * However once an action is complete, the instance
   * will be recycled and reused for another request.
   * 
   *
   * Several ideas are borrowed from the original work of
   * Torsten Curdt.
   *
   * @author Ivelin Ivanov <[EMAIL PROTECTED]>
   */
  public abstract class AbstractXMLFormAction 
    extends ConfigurableComposerAction 
    implements Poolable
  {
  
    public static final String OBJECT_MAP_NEXT_PAGE = "page";  
  
  
    /**
     * The first method which is called
     * when an action is invoked.
     *
     * It is called before population.
     *
     *
     * @return null if the Action is prepared to continue.
     * an objectModel map which will be immediately returned by the action.
     *
     * This method is a good place to handle buttons with Cancel
     * kind of semantics. For example 
     * <pre>return page("input")</pre>
     *
     */
    protected abstract Map prepare();
    
    
    /**
     * Invoked during the form population process 
     *
     * Provides default implementation, which 
     * can be extended or replaced by subclasses
     *
     * Implementations of this method are responsible 
     * for creating and
     * returning the Form object which the action
     * is working on.
     *
     * @return Form the form object this action works with
     *
     */
    protected Form getForm ()
    {
      Form form = Form.lookup( getObjectModel(), getFormId() );
      
      if (form != null) return form;
      else // create new form
      {
        form = new Form( getFormId(), getFormModel() );
        Validator v = getFormValidator();
        form.setValidator ( v );
        form.save ( getObjectModel(), getFormScope() );
        return form;
      }
    }
  
      
    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, 
String src, Parameters params) 
      throws java.lang.Exception 
    {
      // populate action state objects
        redirector_ = redirector;
        resolver_ = resolver;
        objectModel_ = objectModel;
        src_ = src;
        params_ = params;
      
        // find and save the action command
        findCommand();
  
        // ensure that there is a form available
        // through the rest of the flow
        Form form = getForm();
  
        // call the subclass prepare()
        // give it a chance to get ready for action
        Map prepareResult = prepare();
        if ( prepareResult != null ) return prepareResult;
  
        // populate form with request parameters
        // population is automatically followed by validation by default.
        // If this is not the desired behaviour, the Form class can be subclassed
        form.populate( objectModel );
  
        
        return perform();
    }
    
  
    /**
     * Get the command which was submitted with the form.
     * It is extracted from the standard cocoon-action-* request parameter
     *
     */
      public String getCommand()
      {
        return command_;
      }
    
  
      protected void findCommand()
      {
        command_ = null;
        Enumeration enum = getRequest().getParameterNames ();
        while (enum.hasMoreElements ())
          {
            String paramName = (String) enum.nextElement ();
            // search for the command
           if ( paramName.startsWith ( Constants.ACTION_PARAM_PREFIX ) ) 
           {
             command_ = 
               paramName.substring ( Constants.ACTION_PARAM_PREFIX.length(), 
paramName.length() );
           }
        }
      }
      
      
      /**
       * @return the @view attribute of the xmlform form tag.
       * This attribute is used to identify the part(or view)
       * of the model which is used in the specific xmlform
       * document.
       *
       */
      public String getFormView()
      {
        return getForm().getFormView( getObjectModel() ); 
      }
      
  
    
    /**
     * Called to determine the exit point of an action.
     * The pageName is made available in the objectMap,
     * which can be then referenced in the pipeline
     * @param pageName logical name for a next page
     * @return Map a pipeline objectMap containing the pageName
     *
     */
    protected Map page( String pageName )
    {
      Map objectModel = new HashMap();
      objectModel.put( OBJECT_MAP_NEXT_PAGE,  pageName );    
      return objectModel;
    }
    
    /**
     * Invoked after form population
     * unless a Cancel button was pressed, 
     * in which case population is skipped and this method
     * is invoked immediately
     *
     * Semanticly similar to Struts Action.perform()
     *
     * Take appropriate action based on the command
     *
     */
    public abstract Map perform ();
  
  
    protected SourceResolver getSourceResolver()
    {
      return resolver_;
    }
  
    protected Redirector getRedirector()
    {
      return redirector_;
    }
  
    protected Map getObjectModel()
    {
      return objectModel_;
    }
  
    
    protected Parameters getParameters()
    {
      return params_;
    }
    
    protected String getSrc()
    {
      return src_;
    }
    
    protected Request getRequest()
    {
      return (Request) ( getObjectModel().get(ObjectModelHelper.REQUEST_OBJECT) );
    }
    
    protected Session getSession( boolean shouldCreateNew )
    {
      return getRequest().getSession( shouldCreateNew );
    }
    
    protected Session getSession()
    {
      return getSession( true );
    }
  
    
    /**
     * Extract action parameters and 
     * instantiate a new validator based on them.
     *
     * xmlform-validator-schema-ns
     * xmlform-validator-schema
     *
     * Subclasses may override this method
     * to use custom validators
     *
     */
    protected Validator getFormValidator()
    {
      try
        {
          // initialize the Validor with a schema file
          String schNS = getParameters().getParameter("xmlform-validator-schema-ns", 
null);
          String schDoc = getParameters().getParameter("xmlform-validator-schema", 
null);
          
          // if validator params are not specified, then
          // there is no validation by default
          if (schNS == null || schDoc == null ) return null;
  
          Source schemaSrc = getSourceResolver().resolve( schDoc );
          InputSource is = schemaSrc.getInputSource ();
          SchemaFactory schf = SchemaFactory.lookup ( schNS );
          Schema sch = schf.compileSchema ( is );
          
          return sch.newValidator();  
        } 
        catch ( Exception e)
        {
          // couldn't load the validator
          throw new RuntimeException( " Failed loading validating schema ", e );
        }
    }
    
    
    /**
     * Extract xmlform-model 
     * action parameter and 
     * instantiate a new form model it.
     *
     *
     * Subclasses may override this method
     * to use custom model instantiation technique
     *
     */
    protected Object getFormModel()
    {
      try
        {
          String modelClassName = getParameters().getParameter("xmlform-model", null);
          Class modelClass = Class.forName ( modelClassName );
          Object o = modelClass.newInstance ();
          return o;
        } 
        catch ( Exception e)
        {
          throw new RuntimeException( " Failed instantiating form model ", e );
        }
    }
  
    
    protected String getFormId()
    {
      String formId = getParameters().getParameter("xmlform-id", null);
      if ( formId == null )
        throw new RuntimeException( " xmlform-id not specified " );
      else 
        return formId;
    }
      
    
    protected String getFormScope()
    {
      String formScope = getParameters().getParameter("xmlform-scope", null);
      if ( formScope == null )
      {
        // default to request scope
        formScope = Form.SCOPE_REQUEST;
      }
      return formScope;
    }
      
    
    // action state objects
    private Redirector redirector_;
    private SourceResolver resolver_;
    private Map objectModel_;
    private Parameters params_;
    private String src_; 
    private Request request_;
    private Session session_;
    private String command_;
    
  }
  
  
  
  
  1.2       +5 -5      
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/components/modules/database/IfxSerialAutoIncrementModule.java
  
  Index: IfxSerialAutoIncrementModule.java
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/components/modules/database/IfxSerialAutoIncrementModule.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- IfxSerialAutoIncrementModule.java 15 Mar 2002 15:15:08 -0000      1.1
  +++ IfxSerialAutoIncrementModule.java 17 Apr 2002 17:59:15 -0000      1.2
  @@ -60,7 +60,7 @@
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
   import org.apache.avalon.framework.thread.ThreadSafe;
  -import com.informix.jdbc.IfxStatement;
  +//import com.informix.jdbc.IfxStatement;
   
   /**
    * Abstraction layer to encapsulate different DBMS behaviour for autoincrement 
columns.
  @@ -69,10 +69,10 @@
    * (need another one for SERIAL8 ones!)
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Christian Haul</a>
  - * @version CVS $Id: IfxSerialAutoIncrementModule.java,v 1.1 2002/03/15 15:15:08 
haul Exp $
  + * @version CVS $Id: IfxSerialAutoIncrementModule.java,v 1.2 2002/04/17 17:59:15 
ivelin Exp $
    */
  -public class IfxSerialAutoIncrementModule implements AutoIncrementModule, 
ThreadSafe {
  -
  +public abstract class IfxSerialAutoIncrementModule implements AutoIncrementModule, 
ThreadSafe {
  +/*
       public Object getPostValue( Configuration tableConf, Configuration columnConf, 
Configuration modeConf,
                                   Connection conn, Statement stmt, Request request )
           throws SQLException, ConfigurationException {
  @@ -98,5 +98,5 @@
   
           return null;
       };
  -
  +*/
   }
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/samples/xmlform/UserBean.java
  
  Index: UserBean.java
  ===================================================================
  package org.apache.cocoon.samples.xmlform;
  
  import java.util.Set;
  import java.util.HashSet;
  
  import org.w3c.dom.*;
  import javax.xml.parsers.*;
  import javax.xml.transform.*;
  
  /**
   *
   * A sample domain object used as a Form model.
   * Notice that it has mixed content: 
   * JavaBean properties and 
   * DOM Nodes, which are handled correctly by the
   * framework when referenced via XPath.
   *
   */
  
  public class UserBean 
  {
    private String fname = "Donald";
    private String lname = "Duck";
    private String email = "[EMAIL PROTECTED]";
    private int age = 5;
    private int count = 1;
    private short numInstalls = 1; 
    private String liveUrl = "http://";;
    private boolean publish = true;
    private Set roles = new HashSet();
    
    private Node system;
  
    public UserBean ()
    {
      initDomNode();
      initRoleSet();
    }
  
    public String getFirstName() {
      return fname;
    }
    
    public void setFirstName(String newName) {
      fname = newName;
    }
  
    public String getLastName() {
      return lname;
    }
    
    public void setLastName(String newName) {
      lname = newName;
    }
  
    public String getEmail() {
      return email;
    }
  
    public void setEmail(String newEmail) {
      email = newEmail;
    }
  
  
    public String getLiveUrl() {
      return liveUrl;
    }
  
    public void setLiveUrl( String newUrl ) {
      liveUrl = newUrl;
    }
  
    public int getAge() 
      {
      return age;
      }
    
    public void setAge( int newAge ) 
      {
      age = newAge;
      }
    
  
    public short getNumber() 
      {
      return numInstalls;
      }
    
    public void setNumber( short num ) 
      {
      numInstalls = num;
      }
    
    public boolean getPublish() 
      {
      return publish;
      }
    
    public void setPublish( boolean newPublish ) 
      {
      publish = newPublish;
      }
    
    
     public Node getSystem() 
        {
        return system;
       }
  
      public void setSystem( Node newSystem ) 
        {
        system = newSystem;
       }
  
    public int getCount() {
      return count;
    }
  
    public void incrementCount() {
      count++;
    }
  
    public void initDomNode()
    {
      DOMImplementation impl;
      try
      {
        // Find the implementation
        DocumentBuilderFactory factory 
         = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
        factory.setValidating ( false );
        DocumentBuilder builder = factory.newDocumentBuilder();
        impl = builder.getDOMImplementation();
      }
      catch (Exception ex)
      {
        throw new RuntimeException("Failed to initialize DOM factory. Root cause: \n" 
+ ex);
      }
  
      // initialize system as dom node
      Document doc = impl.createDocument( null, "XMLForm_Wizard_System_Node", null);
      Node rootElement = doc.getDocumentElement();
  
      Node os = doc.createElement ( "os" );
      Text text = doc.createTextNode( "Linux" );
      os.appendChild(text);
      rootElement.appendChild( os );
  
      Node processor = doc.createElement ( "processor" );
      text = doc.createTextNode( "p4" );
      processor.appendChild(text);
      rootElement.appendChild( processor );
  
      Attr ram = doc.createAttribute ( "ram" );
      ram.setValue ( "512" );
      NamedNodeMap nmap = rootElement.getAttributes();
      nmap.setNamedItem ( ram );
  
      Node servletEngine = doc.createElement ( "servletEngine" );
      text = doc.createTextNode( "Tomcat" );
      servletEngine.appendChild(text);
      rootElement.appendChild( servletEngine );
  
      Node javaVersion = doc.createElement ( "javaVersion" );
      text = doc.createTextNode( "1.3" );
      javaVersion.appendChild(text);
      rootElement.appendChild( javaVersion );
  
      system = rootElement;
  
    }
    
    public Set getRoles()
    {
      return roles;
    }
    
    public void setRoles( Set newRoles )
    {
      roles = newRoles;
    }
    
    public void initRoleSet()
    {
      roles.add( "Geek" );
      roles.add( "Hacker" );
      roles.add( "Student" );
      roles.add( "University Professor" );
      roles.add( "Developer" );
      roles.add( "Tech Lead" );
      roles.add( "Development Manager" );
      roles.add( "Executive" );
      roles.add( "Heir of the Apache" );
    }
    
  }
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/samples/xmlform/WizardAction.java
  
  Index: WizardAction.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/samples/xmlform/WizardAction.java,v
 1.1 2002/04/17 17:59:15 ivelin Exp $
   * $Revision: 1.1 $
   * $Date: 2002/04/17 17:59:15 $
   *
   * ====================================================================
   * The Apache Software License, Version 1.1
   *
   * 
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, 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 acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", 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 names without prior written
   *    permission of the Apache Group.
   *
   * 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 (INCLUDING, 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 based on software copyright (c) 2001, Plotnix, Inc,
   * <http://www.plotnix.com/>.
   * For more information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.cocoon.samples.xmlform;
  
  
  // Java classes
  import java.util.Map;
  import java.util.HashMap;
  import java.util.SortedSet;
  import java.util.Iterator;
  import java.util.Properties;
  import java.io.InputStream;
  import java.io.FileInputStream;
  import java.io.File;
  
  // XML classes
  import javax.xml.transform.stream.StreamSource;
  import javax.xml.transform.TransformerException;
  import org.xml.sax.InputSource;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  // Framework classes
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.excalibur.pool.Poolable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  // Cocoon classes
  import org.apache.cocoon.environment.Redirector;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.acting.*;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.ObjectModelHelper;
  import org.apache.cocoon.environment.Session;
  import org.apache.cocoon.environment.Context;
  
  // Schematron classes
  import org.apache.cocoon.validation.SchemaFactory;
  import org.apache.cocoon.validation.Schema;
  import org.apache.cocoon.validation.Validator;
  import org.apache.cocoon.validation.Violation;
  
  // Cocoon Form
  import org.apache.cocoon.acting.AbstractXMLFormAction;
  import org.apache.cocoon.xmlform.Form;
  import org.apache.cocoon.xmlform.FormListener;
  
  
  /**
   * This action demonstrates 
   * a relatively complex form handling scenario.
   *
   * @author Ivelin Ivanov <[EMAIL PROTECTED]>
   */
  public class WizardAction 
    extends AbstractXMLFormAction
    implements FormListener
    
  { 
  
  
    // different form views 
    // participating in the wizard
    final String VIEW_START = "start";
    final String VIEW_USERID = "userIdentity";
    final String VIEW_DEPLOYMENT = "deployment";
    final String VIEW_SYSTEM = "system";
    final String VIEW_CONFIRM = "confirm";
    final String VIEW_END = "end";
  
    // action commands used in the wizard
    final String CMD_START = "start";
    final String CMD_NEXT = "next";
    final String CMD_PREV = "prev";
          
    
    /**
     * The first callback method which is called
     * when an action is invoked.
     *
     * It is called before population.
     *
     *
     * @return null if the Action is prepared to continue.
     * an objectModel map which will be immediately returned by the action.
     *
     * This method is a good place to handle buttons with Cancel
     * kind of semantics. For example 
     * <pre>if getCommand().equals("Cancel") return page("input");</pre>
     *
     */
    protected Map prepare() 
    {
      
      if ( getCommand() == null )
        {
          return page( VIEW_START );
        }
      else   if ( getCommand().equals( CMD_START ) )
      {
        // reset state by removing old form
        // if one exists
        Form.remove( getObjectModel(), getFormId() );
        getForm().addFormListener( this );
  
        return page( VIEW_USERID );
      }
  
      
      // get ready for action
      // if not ready return page("whereNext");
      return null;
    }
  
    
    /**
     * Invoked after form population
     *
     * Semanticly similar to Struts Action.perform()
     *
     * Take appropriate action based on the command
     *
     */
    public Map perform ()
    {
  
      // get the actual model which this Form encapsulates
      // and apply additional buziness logic to the model
      UserBean  jBean = (UserBean) getForm().getModel();
      jBean.incrementCount();
  
      // set the page control flow parameter 
      // according to the validation result
      if ( getForm().getViolations () != null )
      {
        // errors, back to the same page
        return page( getFormView() );
      }
      else 
      {
        // validation passed
        // continue with control flow
        
        // get the user submitted command (through a submit button)
        String command = getCommand();
        // get the form view which was submitted
        String formView = getFormView();
  
        // apply control flow rules
        if ( formView.equals ( VIEW_USERID ) )
        {
          if ( command.equals( CMD_NEXT ) )
          {
            return page( VIEW_DEPLOYMENT );
          }
        }
        else if ( formView.equals ( VIEW_DEPLOYMENT ) )
        {
          if ( command.equals( CMD_NEXT ) )
          {
            return page( VIEW_SYSTEM );
          }
          else if( command.equals( CMD_PREV ) )
            return page( VIEW_USERID );
        }
        else if ( formView.equals ( VIEW_SYSTEM ) )
        {
          if ( command.equals( CMD_NEXT ) )
          {
            return page(  VIEW_CONFIRM );
          }
          else if( command.equals( CMD_PREV ) )
            return page( VIEW_DEPLOYMENT );
        }
        else if ( formView.equals ( VIEW_CONFIRM ) )
        {
          if ( command.equals( CMD_NEXT ) )
          {
            return page( VIEW_END );
          }
          else if( command.equals( CMD_PREV ) )
            return page( VIEW_SYSTEM );
        }
      }
  
      // should never reach this statement
      return page( VIEW_START );    
      
    }  
  
    
  
    
    
    /** 
     *
     * FormListener callback 
     * called in the beginning Form.populate()
     * before population starts.
     *
     * This is the place to handle unchecked checkboxes.
     *
     */
    public void reset( Form form )
    {
      // based on the current form view
      // make some decisions regarding checkboxes, etc.
      String formView = getFormView();
      if ( formView.equals ( VIEW_DEPLOYMENT ) )
      {
        // deal with the publish checkbox
        form.setValue( "/publish", Boolean.FALSE );
      }
    }
    
    
    /** 
     * FormListener callback 
     * 
     * Invoked during Form.populate();
     *
     * It is invoked before a request parameter is mapped to
     * an attribute of the form model.
     *
     * It is appropriate to use this method for filtering 
     * custom request parameters which do not reference
     * the model.
     *
     * Another appropriate use of this method is for graceful filtering of invalid
     * values, in case that knowledge of the system state or 
     * other circumstainces make the standard validation 
     * insufficient. For example if a registering user choses a username which
     * is already taken - the check requires database transaction, which is 
     * beyond the scope of document validating schemas. 
     * Of course customized Validators can be implemented to do 
     * this kind of domain specific validation
     * instead of using this method.
     * 
     *
     * @return false if the request parameter should not be filtered.
     * true otherwise.
     */
    public boolean filterRequestParameter (Form form, String parameterName)
    {
      // TBD
      return false;
    }
   
     
    public  String getFile( String FileName ) {
      try
      {
        final String  FILE_PREFIX = "file:";
        String path = getSourceResolver().resolve(FileName).getSystemId();
        if(path.startsWith(FILE_PREFIX))
           path = path.substring(FILE_PREFIX.length());
                return path;
      }
      catch(Exception e)
      {
         getLogger().error("could not read mapping file",e);
        return null;
      }
    }
      
    private Validator validator_ = null;
    private boolean initialized_ = false;
    
  }
  
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/transformation/XMLFormTransformer.java
  
  Index: XMLFormTransformer.java
  ===================================================================
  /*
  
  
  
   ============================================================================
  
                     The Apache Software License, Version 1.1
  
   ============================================================================
  
  
  
   Copyright (C) 1999-2002 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.util.Map;
  import java.util.List;
  import java.util.Iterator;
  import java.util.Collection;
  import java.util.SortedSet;
  import java.util.Stack;
  
  import org.xml.sax.Attributes;
  import org.xml.sax.SAXException;
  import org.xml.sax.helpers.AttributesImpl;
  
  import org.apache.log.Logger;
  import org.apache.avalon.framework.parameters.Parameters;
  
  import org.apache.cocoon.ProcessingException;
  import org.apache.cocoon.precept.acting.AbstractPreceptorAction;
  import org.apache.cocoon.caching.CacheValidity;
  import org.apache.cocoon.caching.NOPCacheValidity;
  import org.apache.cocoon.environment.ObjectModelHelper;
  import org.apache.cocoon.environment.Request;
  import org.apache.cocoon.environment.Session;
  import org.apache.cocoon.environment.SourceResolver;
  import org.apache.cocoon.transformation.AbstractTransformer;
  
  import org.apache.cocoon.validation.Violation;
  import org.apache.cocoon.xmlform.Form;
  
  /**
   * 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
   *
   * The original code was built by Torsten Curdt as 
   * part of the Preceptor API
   *
   * @author: Torsten Curdt <[EMAIL PROTECTED]>
   * @author: Ivelin Ivanov <[EMAIL PROTECTED]>
  
   */
  
  public class XMLFormTransformer extends AbstractTransformer {
  
    //implements Cacheable {
  
  
  
    public final static String NS = "http://xml.apache.org/cocoon/xmlform/2002";;
    private final static String NS_PREFIX = "cform";
    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";
  
    /** 
     * 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_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";
    
    /**
     * 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>
     *   
     * @todo implement
     *
     */
    public final static String TAG_GROUP = "group";
    
    /** 
     *  repeat tag 
     *
     *  <repeat nodeset="/cart/items/item">
     *    <input ref="." .../><html:br/>
     *  </repeat>
     *
     * @todo implement
     *
     */
  
    public final static String TAG_REPEAT = "repeat";
  
    
    /**
     * Stack of nested references.
     * Used to track nested group and repeat tags.
     *
     */
    private Stack nestedRefStack_ = null;
    
    
    // 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";
    
  
    private Map objectModel_;
    private Request request_;
    private Session session_;
  
    // the current Form being processed
    private Form currentForm_;
  
    private Object value_;
  
    private boolean isRootElement_ = true;
    
    public void setup(SourceResolver resolver, Map objectModel, String source, 
Parameters parameters) throws ProcessingException, SAXException, IOException 
    {
      objectModel_ = objectModel;
      request_ = ObjectModelHelper.getRequest(objectModel);
      if (request_ == null) 
        {
        getLogger().debug("no request object");
        throw new ProcessingException("no request object");
        }
      session_ = request_.getSession(false);
      
      // init tracking parameters
      currentForm_ = null;
      nestedRefStack_ = new Stack();
      refStack_ = new Stack();
      currentTagDepth = 0;
    }
  
  
    public void startElement(String uri, String name, String raw, Attributes 
attributes) throws SAXException 
    {
      // track the tree depth
      ++currentTagDepth;
      
      if (isRootElement_)
      {
        AttributesImpl atts = new AttributesImpl( attributes );
        atts.addAttribute( NS, NS_PREFIX, XMLNS_PREFIX + ":" + NS_PREFIX, "CDATA", NS);
        attributes = atts;
        isRootElement_ = false;
      }
      
      // if the element is not in the XMLForm namespace, 
      // ignore it (forward the SAX event and return)
      if (!NS.equals(uri)) 
      {
        super.startElement(uri, name, raw, attributes);
        return;
      }
  
      // 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 ) 
      {
        Entry entry = new Entry( new Integer(currentTagDepth), aref);
        refStack_.push( entry );
      }
      
      // match tag name and apply transformation logic 
      if (TAG_FORM.equals(name)) 
      {
        startElementForm( uri, name, raw, attributes ); 
        return;
      }
      else if ( TAG_OUTPUT.equals(name) ) 
      {
        startElementOutput( uri, name, raw, attributes );
        return;
      } // end if TAG_OUTPUT
      else if ( TAG_CAPTION.equals( name ) ) 
      { 
        super.startElement(uri, name, raw, attributes);
        return;
      }
  
      // if the currentForm_ is still not available
      // then we can't process nested form tags
      if (currentForm_ == null) return;
  
      if (TAG_INSERTVIOLATIONS.equals(name)) 
      {
        startElementViolations( uri, name, raw, attributes );
      } // end if TAG_INSERTVIOLATIONS
  
      else if ( 
                TAG_TEXTBOX.equals(name) ||
                TAG_TEXTAREA.equals(name) ||
                TAG_PASSWORD.equals(name) ||
                TAG_SELECTBOOLEAN.equals(name) ||
                TAG_SELECTONE.equals(name)) 
      {
          startElementSimpleField( uri, name, raw, attributes, currentForm_ );
      }
      else if (TAG_SELECTMANY.equals(name)) 
      {
        //NYI - Not Yet Implemented
        throw new SAXException("tag selectMany Not Yet Implemented");
      }
      else if (
        TAG_SUBMIT.equals(name) ||
        TAG_CANCEL.equals(name) ||
        TAG_RESET.equals(name) ) 
      {
        //NYI
        super.startElement(uri, name, raw, attributes);
      }
      else 
      {
          getLogger().error("unknown element [" + String.valueOf(name) + "]");
          super.startElement(uri, name, raw, attributes);
      }
  } // end of startElement
  
  
    protected void startElementForm(String uri, String name, String raw, Attributes 
attributes) throws SAXException 
    {
      String id = attributes.getValue(TAG_FORM_ATTR_ID);
  
      if ( currentForm_ != null )
      {
        String error = "Form nodes should not be nested ! Current form [id=" + 
currentForm_.getId() + "], nested form [id=" + String.valueOf(id) + "]";
        getLogger().error( error );
        throw new SAXException( error );
      }
  
      super.startElement(uri, name, raw, attributes);
  
      // load up the referenced form 
      currentForm_ =  Form.lookup( objectModel_, id ); 
  
      // if the form wasn't found, we're in trouble
      if (currentForm_ == null)
      {
        getLogger().error("could not find form [id=" + String.valueOf(id) + "]");
      }
    } // end of startElementForm
  
    
    
    protected void startElementViolations(String uri, String name, String raw, 
Attributes attributes) throws SAXException 
    {
        SortedSet violations = currentForm_.getViolations();
  
        // 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 = new AttributesImpl( attributes );
            atts.addAttribute( NS, TAG_COMMON_ATTR_REF, NS_PREFIX + ":" + 
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 (currentForm_ == null)
            {
              throw new SAXException( "When used outside of a form tag, the output tag 
requires an '" + TAG_OUTPUT_ATTR_FORM + "' attribute" );
            }
            form = currentForm_;
          }
          else
          {
            form = Form.lookup( objectModel_, formAttr );
          }
          
          startElementSimpleField( uri, name, raw, attributes, form );
  
    } // end of startElementOutput
    
  
    
    protected void startElementSimpleField(String uri, String name, String raw, 
Attributes attributes, Form form) 
      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 ( form == null)
        {
           throw new SAXException( name + " element should be either nested within a 
form tag or provide a form attribute" );
        }
  
        getLogger().debug("[" + String.valueOf( name ) + "] getting value from form 
[id=" + form.getId() + ", ref=" + String.valueOf(ref) + "]");
  
        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_ + "]") ;
  
        // render the value subelement
        super.startElement(uri, "value", NS_PREFIX + ":" + "value", NOATTR);
        if (value_ != null) 
        {
          String v = String.valueOf( value_ );
          super.characters(v.toCharArray(),0,v.length());
        }
        super.endElement(uri, "value", NS_PREFIX + ":" + "value");
     
    } // end of startElementSimpleField
    
  
    
    public void endElement(String uri, String name, String raw) throws SAXException 
    {
      // track the tree depth
      --currentTagDepth;
      
      // keep the ref stack in synch with the tree navigation
      if ( !refStack_.isEmpty () )
      {
        Entry entry = (Entry) refStack_.peek();
        Integer refDepth = (Integer) entry.getKey ();
        if ( refDepth.intValue () > currentTagDepth ) 
        {
          refStack_.pop();
        }
      }
      
      
      // if the element is not in the XMLForm namespace, 
      // forward the SAX event and return
      if (!NS.equals(uri)) 
      {
        super.endElement(uri, name, raw);
        return;
      }
  
      if (TAG_INSERTVIOLATIONS.equals(name)) 
      {
        // all violations were rendered completely in the startElement method
      }
      else if (TAG_FORM.equals(name)) 
      {
        // nullify currentForm_ since we'return getting out of its scope
        currentForm_ = null;
        super.endElement(uri, name, raw);
      }
      else if (TAG_OUTPUT.equals(name) ||
              TAG_TEXTBOX.equals(name) ||
              TAG_PASSWORD.equals(name) ||
              TAG_SELECTBOOLEAN.equals(name) ||
              TAG_SELECTONE.equals(name) ) 
      {
        super.endElement(uri, name, raw);
      }
      else if (TAG_SELECTMANY.equals(name)) 
      {
        // NYI
      }
      else if (TAG_SUBMIT.equals(name)) 
      {
        super.endElement(uri, name, raw);
      } 
      else if ( TAG_CAPTION.equals( name ) ) 
      { 
        super.endElement(uri, name, raw);
      }
      else 
      {
        getLogger().error("unknown element [" + String.valueOf(name) + "]");
        super.endElement(uri, name, raw);
      }
    } // end of endElement
  
  
  
    public void characters(char[] chars, int start, int len) throws SAXException {
  
      super.characters(chars, start, len);
  
    }
  
  
      /**
       * 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;
        }
  }
  
  
  }
  
  
  
  
  
  1.2       +12 -4     
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/Violation.java
  
  Index: Violation.java
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/Violation.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Violation.java    7 Apr 2002 08:58:27 -0000       1.1
  +++ Violation.java    17 Apr 2002 17:59:15 -0000      1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/Violation.java,v 
1.1 2002/04/07 08:58:27 ivelin Exp $
  - * $Revision: 1.1 $
  - * $Date: 2002/04/07 08:58:27 $
  + * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/Violation.java,v 
1.2 2002/04/17 17:59:15 ivelin Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/17 17:59:15 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -129,14 +129,22 @@
   
       public int compareTo(Object obj)    
       {
  -      if (obj == null) return -1;
  +      if (obj == null) return 1;
         if (obj == this) return 0;
         if ( !(obj instanceof Violation) ) 
             throw new java.lang.IllegalArgumentException( "Can only compare to a 
Violation object" );
         Violation v = (Violation) obj;
         int primaryResult = getPath().compareTo ( v.getPath () );
         if (primaryResult != 0) return primaryResult;
  -      else return (getMessage().compareTo( v.getMessage () ) );
  +      else 
  +      {
  +        if (getMessage () == null)
  +        {
  +          if (v.getMessage() == null) return 0;
  +          else return -1;
  +        }
  +        else return (getMessage().compareTo( v.getMessage () ) );
  +      }
       }
       
       private String xpath_;
  
  
  
  1.2       +6 -5      
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/schematron/SchematronValidator.java
  
  Index: SchematronValidator.java
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/schematron/SchematronValidator.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SchematronValidator.java  7 Apr 2002 08:58:28 -0000       1.1
  +++ SchematronValidator.java  17 Apr 2002 17:59:15 -0000      1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/schematron/SchematronValidator.java,v
 1.1 2002/04/07 08:58:28 ivelin Exp $
  - * $Revision: 1.1 $
  - * $Date: 2002/04/07 08:58:28 $
  + * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/schematron/SchematronValidator.java,v
 1.2 2002/04/17 17:59:15 ivelin Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/17 17:59:15 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  @@ -424,11 +424,12 @@
      */
     public void setProperty (java.lang.String property, java.lang.Object value) 
throws java.lang.IllegalArgumentException
     {
  -    if ( !property.equals ( Validator.PROPERTY_PHASE ) || ( !(value instanceof 
String) ) )
  +    if ( property.equals ( Validator.PROPERTY_PHASE ) && ( value == null || (value 
instanceof String)) ) 
       {
  -      throw new IllegalArgumentException(" Property " + property + " is not 
supported or value is invalid");
  +      phaseProperty_ = (String) value;
       }
  -    else phaseProperty_ = (String) value;
  +    else 
  +      throw new IllegalArgumentException(" Property " + property + " is not 
supported or value is invalid");
     }
     
   }
  
  
  
  1.2       +523 -13   
xml-cocoon2/src/scratchpad/src/org/apache/cocoon/xmlform/Form.java
  
  Index: Form.java
  ===================================================================
  RCS file: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/xmlform/Form.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Form.java 7 Apr 2002 08:58:28 -0000       1.1
  +++ Form.java 17 Apr 2002 17:59:15 -0000      1.2
  @@ -1,11 +1,11 @@
   /*
  - * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/xmlform/Form.java,v 1.1 
2002/04/07 08:58:28 ivelin Exp $
  - * $Revision: 1.1 $
  - * $Date: 2002/04/07 08:58:28 $
  + * $Header: 
/home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/xmlform/Form.java,v 1.2 
2002/04/17 17:59:15 ivelin Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/04/17 17:59:15 $
    *
    * ====================================================================
    * The Apache Software License, Version 1.1
  - *
  + * 
    *
    * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
    * reserved.
  @@ -63,7 +63,31 @@
   package org.apache.cocoon.xmlform;
   
   import java.util.Collection;
  +import java.util.Enumeration;
  +import java.util.SortedSet;
  +import java.util.Set;
  +import java.util.HashSet;
  +import java.util.TreeSet;
  +import java.util.ArrayList;
  +import java.util.Iterator;
  +import java.util.Map;
  +import java.util.Collections;
  +
  +import org.apache.commons.jxpath.JXPathContext;
  +import org.apache.commons.jxpath.Pointer;
  +import org.apache.commons.jxpath.ri.pointers.DOMAttributePointer;
   
  +import org.apache.cocoon.Constants;
  +import org.apache.cocoon.environment.ObjectModelHelper;
  +import org.apache.cocoon.environment.Request;
  +import org.apache.cocoon.environment.Session;
  +import org.apache.cocoon.environment.Session;
  +import org.apache.cocoon.validation.Validator;
  +import org.apache.cocoon.validation.Violation;
  +
  +import org.w3c.dom.*;
  +import javax.xml.parsers.*;
  +import javax.xml.transform.*;
   
   /**
    * <p>
  @@ -72,39 +96,525 @@
    *  FormValidatingAction
    * </p>
    *
  + * NOTE: This class is NOT thread safe
    *
    * @author Ivelin Ivanov, [EMAIL PROTECTED]
  - * @version $Revision: 1.1 $ $Date: 2002/04/07 08:58:28 $
  + * @version $Revision: 1.2 $ $Date: 2002/04/17 17:59:15 $
    */
   
   public class Form
   {
  +
  +  public static String SCOPE_REQUEST = "request";
  +  public static String SCOPE_SESSION = "session";
  +
  +  public static String  FORM_VIEW_PARAM = "cocoon-xmlform-view";
  +             
  +  public static String VIOLATION_MESSAGE_DATA_FORMAT_ERROR  
  +    = "Invalid data format";
     
  +  /**
  +   * an XMLForm is only usable when it has an id and an underlying model
  +   */
  +  public Form( String id, Object model )
  +  {
  +    
  +    if ( (id == null) || (model == null) )
  +      throw new java.lang.IllegalStateException( "Form cannot be created with null 
id or null model " );
  +    setId ( id );
  +    setModel( model );
  +  }
  +
     
  -  public Object getInstance()
  +    
  +  public String getId()
     {
  -    return instance_;
  +    return id_;
     }
     
  -  public void setInstance( Object newInstance )
  +  public void setId( String newId )
     {
  -    instance_ = newInstance;
  +    id_ = newId;
     }
   
   
  -  public Collection getViolation()
  +  public Object getModel()
  +  {
  +    return model_;
  +  }
  +  
  +  public void setModel( Object newModel )
  +  {
  +    model_ = newModel;
  +    jxcontext_ = JXPathContext.newContext( model_ );
  +    jxcontext_.setLenient( false );
  +  }
  +
  +
  +  public Validator getValidator()
  +  {
  +    return validator_;
  +  }
  +  
  +  public void setValidator( Validator newValidator )
  +  {
  +    validator_ = newValidator;
  +  }
  +
  +
  +  public SortedSet getViolations()
     {
       return violations_;
     }
   
  +    
  +  /**
  +   * Encapsulates access to the model
  +   *
  +   * @param xpath to the model attribute
  +   * @param value to be set
  +   */
  +  public void setValue(String xpath, Object value) 
  +  {
  +    if ( model_ == null)
  +      throw new IllegalStateException( "Form model not set" );
  +    Pointer pointer = jxcontext_.locateValue( xpath );
  +    setValue( pointer, value );
  +  }
   
  -  public void setViolation( Collection vs )
  +  
  +  public void setValue(String path, Object[] values) 
  +  {
  +    Pointer pointer = jxcontext_.locateValue( path );
  +    Object property = pointer.getValue();
  +    if ( property == null ) 
  +    {
  +      throw new RuntimeException( "Property for " + path + " is nul. Cannot 
determine type." );
  +    }
  +    // if the property is a collection, set value as array
  +    if ( property instanceof Collection || property.getClass ().isArray () )
  +    {
  +      Object newValue = convertType( values, property.getClass () );
  +      pointer.setValue( newValue );
  +    }
  +    // otherwise set the value of the first element 
  +    // (there shouldn't be other)
  +    // in the values array
  +    else setValue( pointer, values[0] );
  +  }
  +
  +  
  +  protected void setValue( Pointer pointer, Object value)
  +  {
  +    Object property = pointer.getValue();
  +    if ( property != null ) 
  +    {
  +      // handle DOM elements
  +      if (property instanceof Element)
  +      {
  +        String textPath = pointer.asPath() + "/text()";
  +        setValue( textPath, value );
  +      }
  +      else if (property instanceof Text)
  +      {
  +        Text  node = (Text) property;
  +        node.setNodeValue ( value.toString() );
  +      }
  +      else if (pointer instanceof DOMAttributePointer)
  +      {
  +        Attr  node = (Attr) ( ( (DOMAttributePointer)pointer).getBaseValue() );
  +        node.setNodeValue ( value.toString() );
  +      }     
  +      else
  +      {
  +        Object newValue = convertType( value, property.getClass () );
  +        pointer.setValue( newValue );
  +      }
  +    }
  +    // set null value
  +    else pointer.setValue( property );
  +  }
  +  
  +  
  +    /**
  +     * Attempts to convert the request parameter
  +     * String value to the actual model property type
  +     */
  +    public Object convertType(Object value, Class requiredType)
  +    {
  +        value = Types.convert(value, requiredType);
  +        return value;
  +    }
  +  
  +  
  +  /**
  +   * Encapsulates access to the model
  +   *
  +   * @param xpath of the model attribute
  +   *
  +   * @throws RuntimeException if the xpath value 
  +   * has invalid XPath syntax or it doesn't point 
  +   * to an attribute of the model
  +   *
  +   */
  +  public Object getValue(String xpath) 
  +  {
  +    if (model_ == null) 
  +      throw new IllegalStateException( "Form model not set" );
  +    Pointer pointer = jxcontext_.locateValue( xpath );
  +    Object property = pointer.getValue();
  +    if ( property != null)
  +    {
  +      // handle DOM elements
  +      if (property instanceof Element)
  +      {
  +        String textPath = pointer.asPath() + "/text()";
  +        property = getValue( textPath );
  +      }
  +      else if (property instanceof Text)
  +      {
  +        Text  node = (Text) property;
  +        property = node.getData();
  +      }
  +    }
  +    return property;
  +  }
  +
  +  
  +  /**
  +   * Performs complete validation
  +   * of the form model
  +   */
  +  public boolean validate()
     {
  -    violations_ = vs;
  +    return validate( null );
  +  }
  +  
  +  
  +  /**
  +   *
  +   * @param the validation phase
  +   *
  +   * @return
  +   * if validation finishes without any violations, 
  +   * return true otherwise return false and save all violations
  +   *
  +   */
  +  public boolean validate( String phase )
  +  {
  +    if ( validator_ == null ) return true;
  +
  +    validator_.setProperty( Validator.PROPERTY_PHASE, phase );
  +    violations_ = validator_.validate( model_ );
  +    if (violations_ == null) return true;
  +    else return false;
     }
  +
  +
  +  
  +  /**
  +   * <p>
  +   *  Populates an HTML Form POST into the XMLForm model (JavaBean or DOM node).
  +   *
  +   *  Expects that all request parameter names are XPath expressions 
  +   *  to attributes of the model.
  +   *  For each request parameter, finds and assigns its value to the
  +   *  JavaBean property corresponding to the parameter's name
  +   *
  +   *
  +   *  @todo provide a more sophisticated examples with checkboxes, multi choice,
  +   *  radio button, text area, file upload, etc.
  +   * </p>
  +   */
  +    public void populate( Map objectModel )
  +      {
  +        // clean violations_ set
  +        violations_ = null;
  +
  +        // let listeners know that 
  +        // population is about to start
  +        reset();
  +        
  +        // generic data format violations
  +        // gathered during population
  +        SortedSet pviolations = new TreeSet();
  +
  +        Request request =getRequest( objectModel );
  +
  +        Enumeration enum = request.getParameterNames ();
  +        while (enum.hasMoreElements ())
  +          {
  +            String path = (String) enum.nextElement ();
  +
  +            // filter custom request parameter
  +            // not refering to the model
  +            if ( filterRequestParameter( path ) ) continue;
  +
  +            Object[] values = request.getParameterValues ( path );
  +
  +            try
  +            {
  +            setValue( path, values );
  +            }
  +            catch (IllegalArgumentException ex)
  +            {
  +              Violation v = new Violation();
  +              v.setPath( path );
  +              v.setMessage( VIOLATION_MESSAGE_DATA_FORMAT_ERROR );
  +              pviolations.add( v );
  +            }
  +         } // while
  +      
  +       // validate form model
  +      autoValidate( objectModel );
  +      
  +      // merge violation sets
  +      if ( violations_ != null)
  +      {
  +        violations_.addAll( pviolations );
  +      }
  +      else
  +      {
  +        if ( !pviolations.isEmpty () ) violations_ = pviolations;
  +      }
  +
  +    }
  +
  +
  +    /**
  +   *
  +   * Convenience method invoked after populate()
  +   * By default it performs Form model validation.
  +   * If default validation is not necessary or the validation
  +   * criteria needs to be different, subclasses can override
  +   * this method to change the behaviour.
  +   *
  +   */
  +    protected void autoValidate( Map objectModel )
  +    {
  +      // perform validation for the phase 
  +      // which matches the name of the current form view
  +      // if one is available
  +      String formView = getFormView( objectModel );
  +      if ( formView != null) 
  +      {
  +        validate( formView );
  +      }
  +    }
  +    
  +
  +    /**
  +   * filters custom request parameter
  +   * not refering to the model
  +   *
  +   * @todo implement default filtering
  +   * for standard Cocoon parameters
  +   * like cocoon-action[-suffix]
  +   *
  +   */
  +    protected boolean filterRequestParameter( String name )
  +    {
  +    // filter standard cocoon-* parameters
  +    if ( filterDefaultRequestParameter( name ) ) return true;   
  +    
  +    // then consult with FormListeners
  +    Set ls = new HashSet();
  +    ls.addAll( Collections.synchronizedSet ( formListeners_ ) );
  +    Iterator iter = ls.iterator ();
  +    while (iter.hasNext())
  +    {
  +      FormListener fl = (FormListener) iter.next();
  +      // if any of the listeners wants this parameter filtered
  +      // then filter it (return true)
  +      if ( fl.filterRequestParameter( this, name ) ) return true;
  +    }
  +    // if none of the listeners wants this parameter filtered
  +    // then don't filter it
  +    return false;
  +    }
  +  
  +  
  +    /**
  +    * Filters the standard cocoon request parameters.
  +    * If default filtering needs to be different,
  +    * subclasses can override this method.
  +    * It is invoked before all listeners are asked to filter the parameter
  +    */
  +    protected boolean filterDefaultRequestParameter( String paramName )
  +    {
  +      if ( paramName.startsWith ( Constants.ACTION_PARAM_PREFIX ) ) 
  +        return true;
  +      if ( paramName.equals ( FORM_VIEW_PARAM ) ) 
  +        return true;
  +      else  return false;
  +    }
  +    
  + 
  +    /**
  +     * Try to extract from the request
  +     * and return the current form view
  +     */
  +    public String getFormView( Map objectModel )
  +    {
  +      Request request =getRequest( objectModel );
  +      return (String) getRequest( objectModel ).getParameter ( Form.FORM_VIEW_PARAM 
);
  +    }
  +    
  +    
  +  /**
  +   *
  +   * This method is called before 
  +   * the form is populated with request parameters.
  +   *
  +   * Semanticly similar to that of the 
  +   * ActionForm.reset() in Struts
  +   *
  +   * Can be used for clearing checkbox fields,
  +   * because the browser will not send them when
  +   * not checked.
  +   *
  +   * Calls reset on all FormListeners
  +   *
  +   */
  +  protected void reset()
  +  {
  +    // notify FormListeners
  +    Set ls = new HashSet();
  +    ls.addAll( Collections.synchronizedSet ( formListeners_ ) );
  +    Iterator iter = ls.iterator ();
  +    while (iter.hasNext())
  +    {
  +      FormListener fl = (FormListener) iter.next();
  +      fl.reset( this );
  +    }
  +    return;
  +  }
  + 
  +  
  +  /**
  +   * Loads a form from the request or session
  +   *
  +   * @param objectMap
  +   * @param id the form id
  +   */  
  +  public static Form lookup ( Map objectModel, String id )
  +  {
  +    Request request =getRequest( objectModel );
  +    Form form = (Form) request.getAttribute( id );
  +    if (form != null) return form;
  +    else
  +    {
  +      Session session = request.getSession( false );
  +      if (session != null) form = (Form) session.getAttribute( id );
  +      return form;
  +    }
  +  }
  +
  +  
  +  /**
  +   * Removes a form from the request and session.
  +   * This method will remove the attribute bindings
  +   * correspoding to the form id from both request
  +   * and session to ensure that a subsequent 
  +   * Form.lookup will not succeed.
  +   *
  +   * @param objectMap
  +   * @param id the form id
  +   */  
  +  public static void remove ( Map objectModel, String id )
  +  {
  +    Request request =getRequest( objectModel );
  +    request.removeAttribute( id );
  +
  +    Session session = request.getSession( false );
  +    if (session != null) session.removeAttribute( id );
  +  }
  +
  +  
  +  /**
  +   * Saves the form in the request or session
  +   *
  +   * @param objectMap 
  +   * @param isSessionScope if true the form will be bound in the session, otherwise 
request
  +   */
  +  public void save ( Map objectModel, String scope )
  +  {
  +    Request request =getRequest( objectModel );
  +
  +    if ( lookup( objectModel, id_ ) != null )
  +      throw new java.lang.IllegalStateException( "Form [id=" + id_ + "] already 
bound in request or session " );
  +
  +    if (SCOPE_REQUEST.equals ( scope ) )
  +    {
  +      request.setAttribute( id_, this );
  +    }
  +    else // session scope
  +    {
  +      Session session = request.getSession( true );
  +      session.setAttribute( id_, this );
  +    }
       
  -  private Collection violations_ = null;  
  -  private Object instance_ = null;
  +  }
  +  
  +  
  +  /**
  +   * Add another FormListener
  +   *
  +   */
  +  public synchronized void addFormListener( FormListener formListener )
  +  {
  +    formListeners_.add( formListener );
  +  }
  +
  +  /**
  +   * Add another FormListener
  +   *
  +   */
  +  public synchronized void removeFormListener( FormListener formListener )
  +  {
  +    formListeners_.remove( formListener );
  +  }
  +
  +  protected final static Request getRequest( Map objectModel )
  +  {
  +    return (Request)objectModel.get(ObjectModelHelper.REQUEST_OBJECT);
  +  }
  +
  +  
  +  
  +  
  +  
  +  /** the set of violations the model commited during validation */
  +  private SortedSet violations_ = null;  
  +  
  +  /** The data model this form encapsulates */
  +  private Object model_ = null;
  +  
  +  /** The list of FormListeners */
  +  private Set formListeners_ = new HashSet();
  +
  +  /** 
  +   * The unique identifier for this form. Used when form is stored in request 
  +   * or session for reference by other components
  +   *
  +   * @todo a centralized form registry would be helpful to prevent from id collision
  +   */
  +  private String id_ = null;
  +
  +  /**
  +   * The JXPath context associated with the model
  +   * Used to traverse the model with XPath expressions
  +   */
  +  private JXPathContext jxcontext_ = null;
  +
  +  
  +  /**
  +   * Used to validate the content of the model
  +   * at various phases
  +   *
  +   */
  +  private Validator validator_ = null;
  +  
     
   }
   
  
  
  
  1.2       +54 -4     xml-cocoon2/src/scratchpad/webapp/mount/xmlform/README.txt
  
  Index: README.txt
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/scratchpad/webapp/mount/xmlform/README.txt,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- README.txt        4 Apr 2002 06:21:51 -0000       1.1
  +++ README.txt        17 Apr 2002 17:59:15 -0000      1.2
  @@ -15,24 +15,74 @@
   
   1) Automated 2 way binding of HTML Forms to JavaBeans (and DOM nodes) through XPath
   2) Automatic validation of JavaBeans(and DOM nodes) through Schematron schemas
  +3) Intermediate XForms like XML markup for form views
   
   
--------------------------------------------------------------------------------------------
   
  -To try the demos, link to:
  +To try the demo, link to:
   
  -http://localhost:8080/cocoon/mount/xmlform/demo1
  -http://localhost:8080/cocoon/mount/xmlform/demo2
  +http://localhost:8080/cocoon/mount/xmlform/wizard.html
   
   
--------------------------------------------------------------------------------------------
   
   For questions contact:
   
  -Ivelin Ivanov, [EMAIL PROTECTED] or [EMAIL PROTECTED]
  +Ivelin Ivanov, [EMAIL PROTECTED]
   
   
--------------------------------------------------------------------------------------------
   
   Following are copies of the announcement emails send to the Cocoon development 
mailing list.
   [EMAIL PROTECTED] <[EMAIL PROTECTED]>
  +
  +
  +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  +
  +----- Original Message ----- 
  +From: "Ivelin Ivanov" <[EMAIL PROTECTED]>
  +To: <[EMAIL PROTECTED]>
  +Sent: Tuesday, April 16, 2002 
  +Subject: [Announcement] Cocoon Form Handling - XML Form Release 0.8
  +
  +
  +First, I would like to thank everyone who participates in the Form Handling 
discussion.
  +I have learned a lot from this discussion in the last few weeks.
  +
  +There are plenty of great ideas coming from all directions, and some of them 
influenced my
  +thinking significantly.
  +
  +As I have already mention more than once, I have a certain fear that this topic may 
be too large 
  +to handle at once and may eventually wind up as it did several times before 
(Schemox, ExFormular, etc.)
  +I would very much like this time Cocoon to end up with a better overall form 
handling solution, than the one that currently exists. It does not have to be perfect 
from the start.
  +
  +With all tha said, I am presenting to anyone interested the new incarnation of the 
xmlform solution.
  +It has gone through major refactoring based on heavy influence from Torsten and 
Konstantin.
  +I will not advertsise what it is this time. I would instead encourage people who 
are *really* interested
  +in bettering Cocoon, to look at the demo and provided feedback.
  +This time there is only one demo, which is an extension of the survey wizard, 
originally offered by Torsten.
  +
  +Once you build c2 with scratchpad, point to 
  +http://localhost:8080/cocoon/mount/xmlform/wizard.html
  +
  +
  +For those who will take the time to peek in,
  +
  +I would like to request votes on the following:
  +
  +1) Does this solution prohibit further extensions in directions that you might be 
interested?
  +
  +2) Does this solution offer better overall form handling than the existing one for 
C2?
  +
  +3) Can this solution be the base for 2.1?
  +
  +
  +Thanks everyone,
  +
  +Ivelin
  +
  +
  +
  +
  +
   
   
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  
  
  1.3       +33 -94    xml-cocoon2/src/scratchpad/webapp/mount/xmlform/sitemap.xmap
  
  Index: sitemap.xmap
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/src/scratchpad/webapp/mount/xmlform/sitemap.xmap,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- sitemap.xmap      7 Apr 2002 08:58:28 -0000       1.2
  +++ sitemap.xmap      17 Apr 2002 17:59:15 -0000      1.3
  @@ -1,12 +1,13 @@
   <?xml version="1.0"?>
   <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0";>
        <!-- =========================== Components ================================ 
-->
  -     <map:components>
  +     <map:components> 
  +             <map:actions>
  +               <map:action name="WizardAction" 
src="org.apache.cocoon.samples.xmlform.WizardAction"  logger="webapp.xmlform"/>
  +             </map:actions>
                <map:generators default="file"/>
                <map:transformers default="xslt">
  -                     <map:transformer name="castor" 
src="org.apache.cocoon.transformation.CastorTransformer">
  -                             <mapping>castor-mappings/test-mapping.xml</mapping>
  -                     </map:transformer>
  +      <map:transformer name="xmlform" 
src="org.apache.cocoon.transformation.XMLFormTransformer" logger="webapp.xmlform"/>
                </map:transformers>
                <map:readers default="resource"/>
                <map:serializers default="html"/>
  @@ -14,107 +15,45 @@
                <map:matchers default="wildcard">
                        <map:matcher name="wildcard" 
src="org.apache.cocoon.matching.WildcardURIMatcherFactory"/>
                </map:matchers>
  -             <map:actions>
  -               <map:action name="FormBinderAction" 
src="org.apache.cocoon.samples.xmlform.FormBinderAction"/>
  -               <map:action name="ValidationFormAction" 
src="org.apache.cocoon.samples.xmlform.ValidatingFormAction"/>
  -             </map:actions>
        </map:components>
   
  -        <!-- =========================== Resources 
================================= -->
  +  <!-- =========================== Resources ================================= -->
        
  -        <!-- 
  -                Display confirmation page for test2
  -        -->
  -        <map:resources>
  -           <map:resource name="display-success">
  -                  <map:generate src="success-page-Demo2.html"/>
  -                  <map:serialize type="html"/>
  -           </map:resource>
  -        </map:resources>
  -
  -        <!-- =========================== Pipelines 
================================= -->
  -
  -     <map:pipelines>
  -             <map:pipeline internal-only="false">
  -                  <!-- 
  -
  -                          Validating XSLT generator
  -
  -                          Generates a Schematron Validating XSLT
  -                          from your schematron-[doctype].xml file
  -
  -                          Dynamic internal pipeline for Schematron schemas 
originally 
  -                          proposed by Jeremy Quinn <[EMAIL PROTECTED]>
  -
  -                  -->
  -               <map:match pattern="make-validator">
  -                  <map:generate src="schematron/xmlform-sch-report.xml"/>
  -                  <map:transform src="schematron/xmlform-schematron.xsl"/>
  -                  <map:serialize type="xml"/>
  -               </map:match>
  -
  -             </map:pipeline>
  -
  -             <map:pipeline>
  -                        <!--
  -                          Demo1:
  -                          1) The Action binds the HTML form to a JavaBean and 
inserts it in the session
  -                          2) CastorTransformer inserts the JavaBean into the SAX 
stream
  -                          3) Schematron stylesheet validates data and adds results 
to the SAX stream
  -                          4) Stylesheet displays form and errors based on the 
output from 3)
  -                        -->
  -                     <map:match pattern="demo1.html">
  -                             <map:act type="FormBinderAction"/>
  -                             <map:generate src="insertFormBean.xml"/>
  -                             <map:transform type="castor"/>  
  -                             <map:transform src="cocoon:/make-validator"/>
  -                             <!-- map:transform 
src="schematron/xmlform-sch-report.xsl"/ -->
  -                             <map:transform src="formbean2html.xsl"/>
  -                             <map:serialize type="html"/>
  -                     </map:match>    
  -
  -                        <!--
  -                          Demo2:
  -                          1) The Action binds the HTML form to a JavaBean and 
inserts it in the session
  -                          2) The Action uses the Java Schematron Validator to 
perform fast validation
  -                          3) The Action inserts into request the validation result 
(if negative)
  -                          4) Selector branches control depending on the outcome of 
3)
  -                          5) If 3) produced validation errors, then the path is 
similar to Demo1
  -                          6) If 3) did not produce validation errors, then Success 
page is displayed
  -                        -->
  -                     <map:match pattern="demo2">
  -                            <map:act type="ValidationFormAction"/>
  -                            <map:select type="parameter">
  -                                <map:parameter name="parameter-selector-test" 
value="{$nextPage}"/>
  -                                <!-- validation passed, go to next page -->
  -                                <map:when test="success">
  -                                  <map:call resource="display-success"/>
  -                                </map:when>
  -                                <!-- validation failed, go to the same page -->
  -                                <map:otherwise>
  -                                  <map:generate src="insertFormBean-Demo2.xml"/>
  -                                  <map:transform type="castor"/>     
  -                                  <map:transform src="formbean2html-Demo2.xsl"/>
  -                                  <map:serialize type="html"/>
  -                                </map:otherwise>
  -                            </map:select>
  -                     </map:match>    
  -
  -             </map:pipeline>
  -
  -     </map:pipelines>
  -
  -
  -</map:sitemap>
  -<!-- end of file -->
  +  <map:resources>
  +  </map:resources>
   
  +  <!-- =========================== Pipelines ================================= -->
   
  +     <map:pipelines> 
   
  +             <map:pipeline>
   
  +      <!-- A non-trivial example - Feedback Wizard -->
  +                     <map:match pattern="wizard.html">
   
  +        <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"/>
  +
  +            <!-- Content transformation logic -->
  +            <map:generate src="wizard/{page}.xml"/>
  +            <map:transform type="xmlform" label="xml"/>
  +            <map:transform src="stylesheets/wizard2html.xsl"/>
  +            <map:transform src="stylesheets/xmlform2html.xsl"/>
  +            <map:serialize type="html"/>
   
  +        </map:act>
   
  +                     </map:match>    
   
  +             </map:pipeline>
   
  +     </map:pipelines> 
   
  +</map:sitemap>
  +<!-- end of file -->
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/webapp/mount/xmlform/schematron/wizard-xmlform-sch-report.xml
  
  Index: wizard-xmlform-sch-report.xml
  ===================================================================
  <?xml version="1.0" ?>
  <!--
  
        Validating Schematron schema for the xmlform example wizard
    
    Schematron Schema language home page:
    http://www.ascc.net/xml/schematron/
    
        Author: Ivelin Ivanov, [EMAIL PROTECTED], April 2002
  
  -->
  
  <schema ns="http://xml.apache.cocoon/xmlform";  
xmlns="http://www.ascc.net/xml/schematron";>
  
        <title>Schema for the XML Form example</title>
    
      <phase id="userIdentity">
              <p>For user identity information.</p>
              <active pattern="user"/>
      </phase>
      <phase id="deployment">
              <p>For deployment info page.</p>
              <active pattern="dep" />
      </phase>
      <phase id="system">
              <p>For system info page.</p>
              <active pattern="sys" />
      </phase>
      <phase id="confirm">
              <p>For final total validation and tracking 
                  some tricky problems.</p>
              <active pattern="user" />
              <active pattern="dep" />
              <active pattern="sys" />
      </phase>
  
      
        <pattern name="User Info Validation Pattern" id="user">
                <rule context="/firstName">
                        <assert  test="string-length(.) &gt; 3" diagnostics="dname 
dcount">
          First name should be at least 4 characters.
        </assert>
                        <assert  test="string-length(.) &lt; 20">
          First name should be less than 20 characters.
        </assert>
                </rule>
                <rule context="/lastName">
                        <assert  test="string-length(.) &gt; 3" diagnostics="dname 
dcount">
          Last name should be at least 4 characters.
        </assert>
                        <assert  test="string-length(.) &lt; 20">
          Last name should be less than 20 characters.
        </assert>
                </rule>
                <rule context="/email">
                        <assert test="contains( string(.),'@')">
          Email format is invalid.
        </assert>
                </rule>
                <rule context="/age">
                        <assert test="number() &gt; 0 and number(.) &lt; 200">
          Age should be a reasonably big positive number.
        </assert>
                </rule>
        </pattern>
    
        <pattern name="Deployment Information Validation Pattern" id="dep">
                <rule context="/number">
                        <assert  test="number() &gt; 0"> 
          The number of deployments must be non-negative ( hopefully positive :-> ) .
        </assert>
                </rule>
                <rule context="/">
        <!-- 
          If the site is to be published, then verify the URL.
          Note: This assertion demonstrates the unique ability of 
          Schematron to test document node dependencies.
          This is not possible to do with XML Schema and Relax NG.
         -->
                        <assert  test="not(string(publish) = 'true') or 
(starts-with(liveUrl, 'http://') and contains( string(liveUrl),'.') ) "> 
          The URL of the published site is invalid.
        </assert>
                </rule>
        </pattern>
    
        <pattern name="System Information Validation Pattern" id="sys">
                <rule context="/system/@ram">
                        <assert test="number() &gt; 0"> 
          The RAM value should be a positive number, denoting the memory in MB (e.g. 
128, 512, etc.).
        </assert>
                </rule>
        </pattern>
  
  </schema>
  
  
  
  
  
  
  
  
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/webapp/mount/xmlform/stylesheets/wizard2html.xsl
  
  Index: wizard2html.xsl
  ===================================================================
  <?xml version="1.0" encoding="UTF-8"?>
  <!--
        Cocoon Feedback Wizard XMLForm processing and displaying stylesheet.    
    
    This stylesheet merges an XMLForm document into 
    a final document. It includes other presentational
    parts of a page orthogonal to the xmlform.
  
    Author: Ivelin Ivanov, [EMAIL PROTECTED], April 2002
        Original Author: Konstantin Piroumian, [EMAIL PROTECTED], 21-Feb-2002
  
  -->
  
  <xsl:stylesheet 
      version="1.0" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
      xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";
      exclude-result-prefixes="xalan"
  >
  
        <xsl:template match="document">
                <html>
                        <head>
                                <title>XMLForm - Cocoon Feedback Wizard</title>        
 
                                <style type="text/css">
                                <![CDATA[
                H1{font-family : sans-serif,Arial,Tahoma;color : 
white;background-color : #0086b2;} 
                BODY{font-family : sans-serif,Arial,Tahoma;color : 
black;background-color : white;} 
                B{color : white;background-color : #0086b2;} 
                HR{color : #0086b2;}
                                      input { background-color: #FFFFFF; color: 
#000099 }               
                                      table { background-color: #EEEEEE; color: 
#000099; font-size: x-small; border: 2px solid brown;}
                                select { background-color: #FFFFFF; color: #000099 }
                                 .error { color: #FF0000; }           
                                 .invalid { color: #FF0000; border: 2px solid #FF0000; 
}
                                 .info { color: #0000FF; border: 1px solid #0000FF; }
                                       .table { border: 1px inset #999999;border: 1px 
inset #999999; width: 500px; }
                   .sub-table { border: none; }
                 .button { background-color: #FFFFFF; color: #000099; border: 1px 
solid #666666; width: 70px; }
                ]]>
                                </style>
                        </head>
                        <body>
            <xsl:apply-templates />
                        </body>
                </html>
        </xsl:template>
        
        <xsl:template match="xf:form">
      <xf:form method="post">
        <xsl:copy-of select="@*" />
          <br/><br/><br/><br/>
          <table align="center">
            <tr>
              <td align="center" colspan="3">
                  <h1>
                      <xsl:value-of select="xf:caption"/>
                      <hr/>
                  </h1>
              </td>
            </tr>
            <xsl:if test="count(error/xf:violation) > 0">
                <tr>
                  <td align="left" colspan="3" class="{error/xf:violation[1]/@class}">
                      <p>
                      * There are
                      [<b><xsl:value-of select="count(error/xf:violation)"/></b>]
                      errors. Please fix these errors and submit the form again.
                      </p>
                      <p>
                        <xsl:for-each select="error/xf:violation[string(@ref)='']">
                          * 
                          <xsl:value-of select="." />
                          <br/>
                        </xsl:for-each>
                      </p>
                      <p/>
                  </td>
                </tr>
            </xsl:if>
           <xsl:for-each select="*[name() != 'xf:submit']">
            <xsl:choose>
              <xsl:when test="name() = 'error'"/>
              <xsl:when test="xf:*">
                 <tr>
                    <td>
                      <xsl:value-of select="xf:caption" />
                    </td>
                    <td>
                      <xsl:copy-of select="." />
                    </td>
                    <td class="{xf:violation[1]/@class}">
                      <xsl:for-each select="xf:violation">
                        * <xsl:value-of select="." />
                        <br/>
                      </xsl:for-each>
                    </td>
                  </tr>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:copy-of select="."/>
              </xsl:otherwise>
             </xsl:choose>
           </xsl:for-each>
          <tr>
            <td align="center" colspan="3">
              <xsl:for-each select="*[name() = 'xf:submit']">
                <xsl:copy-of select="." />
                <xsl:text> </xsl:text>
              </xsl:for-each>
            </td>
          </tr>
        </table>
      </xf:form>
        </xsl:template>
      
        <xsl:template match="xf:output">
      <div align="center"> 
          <hr width="30%"/>
          <br/>
          <font size="-1">
            <code>
              <xsl:value-of select="xf:caption" /> :
              <xsl:copy-of select="." />
            </code>
          </font>
          <br/>
      </div>
        </xsl:template>
        
        <xsl:template match="*">
            <xsl:copy-of select="." />
        </xsl:template>
      
  </xsl:stylesheet>
  
  
  
  
  1.1                  
xml-cocoon2/src/scratchpad/webapp/mount/xmlform/stylesheets/xmlform2html.xsl
  
  Index: xmlform2html.xsl
  ===================================================================
  <?xml version="1.0" encoding="iso-8859-1" ?>


<!-- 

        Generic XMLForm 
processing stylesheet.  
  Converts XMLForm tags to HTML tags.
  
  Syntax is borrowed 
from the XForms standard.
  http://www.w3.org/TR/2002/WD-xforms-20020118/
  
  This 
stylesheet is usually applied at the end of a 
  transformation process after laying 
out the xmlform
  tags on the page is complete. At this stage xmlform tags 
  are 
rendered in device specific format.
  
  Author: Ivelin Ivanov, [EMAIL PROTECTED], 
April 2002
        Original Author: Torsten Curdt, [EMAIL PROTECTED], March 2002

-->

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
  
  xmlns:xf="http://xml.apache.org/cocoon/xmlform/2002";
>

   <xsl:output method = 
"html" omit-xml-declaration = "yes"  /> 
  

   <xsl:template match="/">
     
<xsl:apply-templates />
   </xsl:template>

   <xsl:template match="xf:form">
      
<form method="POST">
         <xsl:copy-of select="@*"/>
         <input type="hidden" 
name="cocoon-xmlform-view" value="{@view}"/>
         <xsl:apply-templates />
      
</form>
   </xsl:template>

   <xsl:template match="xf:output">
      [<xsl:value-of 
select="xf:value/text()"/>]
   </xsl:template>

   <xsl:template match="xf:textbox">
  
    <input name="{@ref}" type="textbox" value="{xf:value/text()}" />
   
</xsl:template>

   <xsl:template match="xf:password">
      <input name="{@ref}" 
type="password" value="{xf:value/text()}" />
   </xsl:template>

   <xsl:template 
match="xf:selectBoolean">
      <input name="{@ref}" type="checkbox" value="true">
    
    <xsl:if test="xf:value/text() = 'true'">
          <xsl:attribute name="checked"/>
        </xsl:if>
      </input>
   </xsl:template>

   <xsl:template 
match="xf:selectOne">
     <select name="{@ref}">
       <xsl:variable name="selected" 
select="xf:value/text()"/>
       <xsl:for-each select="xf:item">
         <option 
value="{xf:value}">
           <xsl:if test="$selected = xf:value">
             
<xsl:attribute name="selected"/>
           </xsl:if>
           <xsl:value-of 
select="xf:caption"/>
         </option>
       </xsl:for-each>
     </select>
   
</xsl:template>


   <xsl:template match="xf:selectMany">
     <!-- @todo -->
   
</xsl:template>


   <xsl:template match="xf:submit">
      <input 
name="cocoon-action-{@id}" type="submit" value="{xf:caption/text()}" />
   
</xsl:template>


   <xsl:template match="*">
      <xsl:copy><xsl:copy-of select="@*" 
/><xsl:apply-templates /></xsl:copy>
   </xsl:template>

   <xsl:template 
match="text()">
      <xsl:value-of select="." />
   </xsl:template>

</xsl:stylesheet>



  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     [EMAIL PROTECTED]
To unsubscribe, e-mail:          [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to