The Precept Stuff
-----------------

This is all still far from being ready for prime time but in order to share efforts I think
it makes sense to have it in scratchpad. I'll give a short indroduction about the idea, the
terms I am using, what is still missing, what should change and how I see its future.

Installation
------------

Add the roles and xconf snippet to you installation, set your precptor path (it's not yet
resolved) mount the sitemap and access the url "app/demo.html" relative to the sitemap mount.


Introduction
------------ 

Form processing is always about collecting data of a specific structure. When this data
selection process is finished the data will be used for some kind of purpose. Seeing this
from a more concrete XMLish point of view one could say we are building some kind of XML
instance which needs to conform to a schema description. That's exactly the major goal of
this approach.


The model
---------

At the start of the flow we create some kind of instance that can hold our values. This
instance will be held inside the users session until the end of form processing. In order to
specify the structure and validation of the form data we need some kind of schema. Since
"schema" is widely used as the short form for the w3.org XML Schema language I have chosen to
use a different term that means schemas in general (XML Schema, relax-ng, etc.): "preceptors".
In the first place the preceptor defines the structure of the instance but also holds 
information about the value constraints. That's our model.


The view
--------

Especially for multipage forms (sometimes called "wizards") it is quite obvious that only parts 
of an instance are supposed to be displayed.

  [-----------------instance---------------]
  [---view1---][----view2----][----view3---]

So we have different views of the instance. There are 2 different ways of achieving this. 
(see webapp example1 and example2 for more details)


The controller
--------------

The controller needs to populate request parameters into the instance and depending on the
validation result select the correct following view. At the beginning I was quite optimistic
this could all happen automagically - now I know better. I experienced two problems that are 
not to solve automagically:

1. checkboxes - Arrggh! This is the most stupid behavior I have ever seen... Anyway we have
   to deal with it. The only really working solution is that the controller "knows" when a 
   checkbox value is supposed to be in the request. Otherwise you cannot turn off your 
   checkboxes because a turned off checkbox will not appear in the request.

2. partial validation - It's not true that you always want to validate a complete view.
   Never thought this would be necessary until some people showed me a real world example.

The current approach is to bind methods to the form submit buttons. So you can define
explicitly what should happen (populated, validated, which view is supposed to be shown) on a 
specific button. So you are free to do whatever you want on a button click...


This usually will happen inside an multiaction (maybe also within Ovidiu's schecoon?)

 ...
 public Map introspection(Redirector redirector, Sou...
    getLogger().debug("start of flow");
    // start of flow create session
    Session session = createSession(objectModel);
    // create instance
    Instance instance = createInstance("feedback");
    // save instance into session
    session.setAttribute("form-feedback",instance);
    // select first view
    return(page(VIEW1));
  }                                                                                                
 ...
 public Map doNext(Redirector redirector, Sou...
    getLogger().debug("populating");
    // populate a set of data into the instace
    populate(objectModel, "form-feedback", SET_INSTALLATION );
    // check if there are errors in those fields
    List errors = validate(objectModel, "form-feedback", SET_INSTALLATION );
    if(errors != null) {
      // errors - go back to last view
      getLogger().debug("some constraints FAILED");
      return (page(VIEW1));
    }
    else {
      // errors - go to next view
      getLogger().debug("all constraints are ok");
      return (page(VIEW2));
    }
  }                     
 ...


The Instance
------------

You can drop in any instance implementation that conforms to the following interface:

public interface Instance extends Component {
  public void setValue(String xpath, Object value);
  public void setValue(String xpath, Object value, Context context);

  public Object getValue(String xpath);

  public List validate(String xpath, Context context);
  public List validate(Context context);

  public void setPreceptor( Preceptor preceptor );
  public Preceptor getPreceptor();
 
  public void toSAX( ContentHandler handler, boolean withConstraints);
  public long getLastModified();
}                                       

Note: I removed the Exceptions for this README. Please use the Instance.java as reference.

Currently I have written a (very) simple dom implementation. (without namespace support or 
any other features. More a simple tree. But should be quite fast)
And the first step towards a BeanInstance. (Validation is missing and needs some more 
discussion)

  ...
  <instance-impl>
    <component-instance name="dom" class="...">
    <component-instance name="bean" class="...">
      <class mapping="...">somwhere.my.bean</class>
    </component-instance>
  </instance-impl> 
  ...


The Preceptor
-------------

A preceptor is usually some kind of wrapper around or interface to one of the well known
validators. If you write a preceptor e.g. for XML Schema you can easily drop it in and 
design your form within an XSD.

public interface Preceptor extends Component {
  public List getConstraitsFor( String xpath );
  public boolean isValidNode( String xpath );
  public void buildInstance( Instance instance );
} 

Note: I removed the Exceptions for this README. Please use the Instance.java as reference.

The mapping between the instance and the preceptor looks like this:

  <instances logger="webapp.validation">
    <!-- dom instance without validation -->
    <instance name="empty" impl="dom"/>

    <!-- dom instance with an easyrelax preceptor -->
    <instance name="feedback" impl="dom">
      <preceptor impl="easyrelax" uri="file:/D:/...../model/easyrelax.xml"/>
    </instance>

    <!--
    <instance name="form1" impl="dom">
      <preceptor impl="xsd" uri="..."/>
    </instance>
    <instance name="form2" impl="bean">
      <preceptor impl="relax-ng" uri="..."/>
    </instance>
    -->
  </instances>  


The Constraint
--------------

A constraint restricts valid values of nodes inside the instance for a given context.  If we
don't want to write our own constraints (and this should be the goal) we need to wrap
existing ones into the Constraint Interface.

public interface Constraint {
  public boolean isSatisfiedBy( Object value, Context context );
  public String getId();
  public String getType();
  public void toSAX(ContentHandler handler) throws SAXException;
} 


TODOs
-----

* make the easyrelax PreceptorBuilder use the parser component
* put the easyrelax ConstraintFactory under CM control
* use the real configuration values in the easyrelax constraints
* implement the getLastModified in the simple dom instance
* use the resolver to lookup the mapping for the bean instance
* implement the validation for the bean instance
* implement the getLastModified in the bean instance
* maybe pass the validation result as request attribute to the
  instance transformer
* discuss the selectMany instance representation
* discuss schematron integration
* implement the following tags in the instance transformer
    <textbox ref="xpath">
    <password ref="xpath">
    <selectOne ref="xpath">
    <selectMany ref="xpath">
    <selectBoolean ref="xpath">
    <output ref="xpath">



The future
----------

1) What I like to see in the future is a way to define simple form controllers not in java
   but in XML. This should be quite easy to achieve by writing an action taking an XML
   desciptor as configuration.

2) A better multiaction integration in the sitemap. So those ugly action-set definitions go
   away.

3) A tight integration with Schecoon

4) Talk with with guys from Xerces2 (xsd) about the Preceptor stuff. IIRC they are about to
   rewrite some of the validation stuff anyway.

5) Talk with the guys from Jing (relax-ng) about the Preceptor stuff. AFAICS they currently
   only have per document validation.

6) add a toJavaScript() method to the Constraint interface so we can even generate JavaScript
   validation from the Constraints
