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) ... somwhere.my.bean ... 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: 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 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