You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by "Niall Pemberton (JIRA)" <ji...@apache.org> on 2006/07/19 15:31:30 UTC

[jira] Reopened: (VALIDATOR-125) Validator extension to support extending forms and fields

     [ http://issues.apache.org/jira/browse/VALIDATOR-125?page=all ]

Niall Pemberton reopened VALIDATOR-125:
---------------------------------------

             

> Validator extension to support extending forms and fields
> ---------------------------------------------------------
>
>                 Key: VALIDATOR-125
>                 URL: http://issues.apache.org/jira/browse/VALIDATOR-125
>             Project: Commons Validator
>          Issue Type: Improvement
>         Environment: Operating System: other
> Platform: Other
>            Reporter: Tal Lev-Ami
>            Priority: Minor
>
> Problem:
> In large progject many fields need to be validated and the same field can 
> appear in multiple forms. This create "code" duplication when the field 
> definitions must be redeclared for each form. In some cases two forms are almost
> identical with difference of a field or two. 
> Solution:
> 1. Add a new attribute to the form tag - extends. The form referenced in the
> extends attribute is considered the "super-form" of the new form. This is
> very similar to the way that a form in a formset with a specific locale can
> extend a form in the default formset. Locale search order is kept when
> searching the extended form.
> 2. Add a new attribute to the field tag - uses. The format of the attribute
> is <formname>.<fieldname> A field that specifies this attribute is replaced
> with the field called "fieldname" in the form called "formname". Locale
> search order is kept.
> Implementation:
> Extends the ValidationResources.processForms() to rebuild all the forms
> taking the uses and extend fields into account.
> Open Issues:
> How can error handling be performed inside the validator package (e.g. the
> form or field extended do not exist)?
> Patch (works but does not report errors):
> Index: src/share/org/apache/commons/validator/Field.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/validator/src/share/org/apache/commons/validator/Field.java,v
> retrieving revision 1.5
> diff -u -r1.5 Field.java
> --- src/share/org/apache/commons/validator/Field.java	30 Mar 2002 04:33:17 -
> 0000	1.5
> +++ src/share/org/apache/commons/validator/Field.java	14 Oct 2002 17:36:50 -
> 0000
> @@ -102,6 +102,7 @@
>      protected String indexedListProperty = null;
>      protected String key = null;
>      protected String depends = null;
> +    protected String uses = null;
>      protected int page = 0;
>      protected int fieldOrder = 0;
>      
> @@ -414,6 +415,25 @@
>      public void setKey(String key) {
>         this.key = key;
>      }
> +
> +	/**
> +	 * Returns the name of the field to use.
> +	 * of this field.
> +	 * @return String
> +	 */
> +	public String getUses() {
> +		return uses;
> +	}
> +
> +	/**
> +	 * Set the name of the field to use. All the attributes, args, vars, 
> etc.
> +	 * will be taken from that field except the name of the property.
> +	 * @param uses The field to use.
> +	 */
> +	public void setUses(String uses) {
> +		this.uses = uses;
> +	}
> +
>      
>      /**
>       * If there is a value specified for the indexedProperty field then 
> @@ -625,6 +645,7 @@
>         results.append("\t\tdepends=             " + depends    + "\n");
>         results.append("\t\tpage=                " + page    + "\n");
>         results.append("\t\tfieldOrder=          " + fieldOrder    + "\n");
> +       results.append("\t\tuses=                " + uses    + "\n");
>  
>         if (hVars != null) {
>            results.append("\t\tVars:\n");
> @@ -640,5 +661,4 @@
>      	
>         return results.toString();
>      }
> -    
>  }
> Index: src/share/org/apache/commons/validator/Form.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/validator/src/share/org/apache/commons/validator/Form.java,v
> retrieving revision 1.3
> diff -u -r1.3 Form.java
> --- src/share/org/apache/commons/validator/Form.java	30 Mar 2002 04:33:17 -
> 0000	1.3
> +++ src/share/org/apache/commons/validator/Form.java	14 Oct 2002 17:36:49 -
> 0000
> @@ -86,6 +86,11 @@
>       * stored under.
>      */
>      protected String name = null;
> +    
> +    /**
> +     * The form this form extends
> +     */
> +    protected String extend = null;
>  
>      /**
>       * List of <code>Field</code>s.  Used to maintain 
> @@ -100,6 +105,7 @@
>      */
>      protected FastHashMap hFields = new FastHashMap();
>  
> +    
>      /**
>       * Gets the name/key of the set of validation rules.
>      */
> @@ -161,6 +167,10 @@
>         
>         results.append("Form: ");
>         results.append(name);
> +       if (extend != null) {
> +       	  results.append(" extends ");
> +       	  results.append(extend);
> +       }
>         results.append("\n");
>  
>         for (Iterator i = lFields.iterator(); i.hasNext(); ) {
> @@ -172,4 +182,21 @@
>         return results.toString();
>      }
>  	
> +	/**
> +	 * Returns the extended form.
> +	 * @return String
> +	 */
> +	public String getExtends() {
> +		return extend;
> +	}
> +
> +	/**
> +	 * Sets the form to extend. All the attributes of the form that are not
> +	 * specifically define in this form are imported.
> +	 * @param extend The extended form
> +	 */
> +	public void setExtends(String extend) {
> +		this.extend = extend;
> +	}
> +
>  }
> Index: src/share/org/apache/commons/validator/ValidatorResources.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/validator/src/share/org/apache/commons/validator/ValidatorResources.java
> ,v
> retrieving revision 1.6
> diff -u -r1.6 ValidatorResources.java
> --- src/share/org/apache/commons/validator/ValidatorResources.java	30 Mar 
> 2002 04:33:17 -0000	1.6
> +++ src/share/org/apache/commons/validator/ValidatorResources.java	14 Oct 
> 2002 17:36:49 -0000
> @@ -112,6 +112,11 @@
>      * The default locale on our server.
>     */
>     protected static Locale defaultLocale = Locale.getDefault();
> +   
> +   /**
> +    * The maximum nesting depth for extends and uses
> +    */
> +   protected static int MAX_DEPTH = 30;
>  
>     /**
>      * Add a <code>FormSet</code> to this <code>ValidatorResources</code>
> @@ -305,15 +310,74 @@
>     }
>  
>     /**
> -    * <p>Process the <code>Form</code> objects.  This clones the 
> <code>Field</code>s 
> -    * that don't exist in a <code>FormSet</code> compared to the default 
> -    * <code>FormSet</code>.</p>
> +    * <p>Process the <code>Form</code> objects. This handles locale support and
> +    * "extends" and "uses" support.<br/>Locale is supported by cloning the 
> +    * <code>Field</code>s that don't exist in a <code>FormSet</code> compared 
> +    * to the default <code>FormSet</code>.<br/>Extends is supported by 
> +    * importing all the attributes of the extended form recusively.<br/>Uses 
> is 
> +    * supported by cloning the used field and overriding its property name.
> +    * </p>
>     */
>     public void processForms() {
>        //hFormSets.put(buildKey(fs), fs);
>        String defaultKey = defaultLocale.toString();
> +
> +	  // First handle extends, it must be done before uses and locale are 
> +	  // handled.
> +      for (Iterator i = hFormSets.keySet().iterator(); i.hasNext(); ) {
> +     	String key = (String)i.next();
> +     	FormSet fs = (FormSet)hFormSets.get(key);
> +        for (Iterator x = fs.getForms().keySet().iterator(); x.hasNext(); ) {
> +           String formKey = (String)x.next();
> +           Form form = (Form)fs.getForms().get(formKey);
> +         
> + 		   // If the form extends another form, create a new form that 
> is
> + 		   // a merge of this form and the exteded one.        
> +           if (form.getExtends() != null) {
> +	           Form newForm = new Form();
> +    	       newForm.setName(form.getName());
> +               mergeFormExtends(fs, form, newForm);
> +	           fs.addForm(newForm);
> +           }            
> +         }
> +	  }         
> +
> +	  // Handle uses
> +      for (Iterator i = hFormSets.keySet().iterator(); i.hasNext(); ) {
> +     	String key = (String)i.next();
> +     	FormSet fs = (FormSet)hFormSets.get(key);
> +        for (Iterator x = fs.getForms().keySet().iterator(); x.hasNext(); ) {
> +           String formKey = (String)x.next();
> +           Form form = (Form)fs.getForms().get(formKey);
> +
> +           Form newForm = new Form();
> +           newForm.setName(form.getName());
> +           
> +           for (Iterator fields = form.getFields().iterator(); fields.hasNext
> (); ) {
> +              Field field = (Field) fields.next();
> +              String fieldKey = field.getKey();
> +
> +              if (field.getUses() != null) {
> +				 Field usedField = getUsedField(fs, field);
> +				 if (usedField != null) { 
> +					usedField = (Field)usedField.clone();
> +					usedField.setProperty(field.getProperty
> ());
> +					// Key must be reset.
> +					usedField.setKey(field.getKey());
> +                    newForm.addField(usedField);
> +				 } else {
> +				 	//[:TODO:] How should error handling be 
> done?
> +				 }
> +              } else {
> +		         newForm.addField((Field)form.getFieldMap().get
> (fieldKey));              	
> +              }
> +           }
> +            
> +           fs.addForm(newForm);
> +         }
> +	  }         
>        
> -      // Loop through FormSets
> +      // Handle Locale. Loop through FormSets
>        for (Iterator i = hFormSets.keySet().iterator(); i.hasNext(); ) {
>           String key = (String)i.next();
>           FormSet fs = (FormSet)hFormSets.get(key);
> @@ -335,17 +399,7 @@
>              // If they don't exist in the current locale's form, then clone 
> them.
>              Form defaultForm = get(defaultLocale, formKey);
>  
> -            for (Iterator defaultFields = defaultForm.getFields().iterator(); 
> defaultFields.hasNext(); ) {
> -               Field defaultField = (Field)defaultFields.next();
> -               String fieldKey = defaultField.getKey();
> -
> -               if (form.getFieldMap().containsKey(fieldKey)) {
> -                  newForm.addField((Field)form.getFieldMap().get(fieldKey));
> -               } else {
> -                  Field field = getClosestLocaleField(fs, formKey, fieldKey);
> -                  newForm.addField((Field)field.clone());   
> -               }
> -            }
> +			mergeForms(fs, defaultForm, form, newForm);
>              
>              fs.addForm(newForm);
>           }
> @@ -360,6 +414,89 @@
>           }
>        }
>   
> +   }
> +
> +   /** 
> +    * Merge the origForm with the form it extends into newForm
> +    */
> +   protected void mergeFormExtends(FormSet fs, Form origForm, Form newForm) {
> +   	  mergeFormExtends(fs, origForm, origForm, newForm, 0);
> +   }
> +
> +   /** 
> +    * Recursively merge currentForm and the form it extends with origForm into
> +    * newForm.
> +    */
> +   private void mergeFormExtends(FormSet fs, Form currentForm, 
> +   								  Form 
> origForm, Form newForm, int depth) {
> +   	  if (depth > MAX_DEPTH) {
> + 		//[:TODO:] How should error handling be done?   	  	
> 	
> +   	  	return;	
> +   	  }								 	
> +      mergeForms(fs, currentForm, origForm, newForm);
> +      if (currentForm.getExtends() != null) {
> +	   	 Form extendedForm = get(fs.getCountry(), fs.getLanguage(),
> +           	                  fs.getVariant(), currentForm.getExtends());
> +         if (extendedForm != null) {
> +            mergeFormExtends(fs, extendedForm, origForm, newForm, depth + 1);
> +         } else {
> +	 		//[:TODO:] How should error handling be done?
> +		 }
> +      }
> +   }
> +
> +   /**
> +    * Clone all the fields of baseForm that are not in origForm into newForm
> +    */
> +   protected void mergeForms(FormSet fs, Form baseForm, Form origForm, Form 
> newForm) {
> +   	  String baseFormKey = baseForm.getName();
> +	  for (Iterator baseFields = baseForm.getFields().iterator(); 
> baseFields.hasNext(); ) {
> +	     Field baseField = (Field)baseFields.next();
> +	     String fieldKey = baseField.getKey();
> +
> +		 // If the origForm already contained the field, clone is from 
> there
> +		 // Otherwise take the closet field based on the locale from 
> the 
> +		 // baseForm
> +	     if (origForm.getFieldMap().containsKey(fieldKey)) {
> +	        newForm.addField((Field)origForm.getFieldMap().get(fieldKey));
> +	     } else {
> +	        Field field = getClosestLocaleField(fs, baseFormKey, fieldKey);
> +	        newForm.addField((Field)field.clone());   
> +	     }
> +	  }   	
> +   }
> +
> +   /**
> +    * Parse the uses attribute of field the return the pointed field.
> +    * Done recursively incase the used field uses another field.
> +    */
> +   protected Field getUsedField(FormSet fs, Field field) {
> +      return getUsedField(fs, field, 0);
> +   }
> +
> +   /**
> +    * Parse the uses attribute of field the return the pointed field.
> +    * Done recursively incase the used field uses another field.
> +    */
> +   protected Field getUsedField(FormSet fs, Field field, int depth) {
> +   	  if (depth > MAX_DEPTH) {
> + 		//[:TODO:] How should error handling be done?   	  	
> 	
> +	  	return null;   	  	
> +   	  }
> +   	  String uses = field.getUses();
> +   	  int sepIndex = uses.indexOf(".");
> +   	  String usedFormKey = uses.substring(0, sepIndex);
> +   	  String usedFieldKey = uses.substring(sepIndex + 1);
> +   	  Field usedField = getClosestLocaleField(fs, usedFormKey, 
> usedFieldKey);
> +	  if (usedField == null) {
> + 		//[:TODO:] How should error handling be done?   	  	
> 	
> +	  	return null;
> +      }
> +   	  
> +   	  if (usedField.getUses() != null) {
> +   	  	usedField = getUsedField(fs, usedField, depth + 1);
> +   	  }
> +   	  return usedField;
>     }
>  
>     /**
> Index: src/test/org/apache/commons/validator/validator-name-required.xml
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/validator/src/test/org/apache/commons/validator/validator-name-
> required.xml,v
> retrieving revision 1.2
> diff -u -r1.2 validator-name-required.xml
> --- src/test/org/apache/commons/validator/validator-name-required.xml	13 Mar 
> 2002 05:39:32 -0000	1.2
> +++ src/test/org/apache/commons/validator/validator-name-required.xml	14 Oct 
> 2002 17:36:49 -0000
> @@ -6,15 +6,24 @@
>                   
> methodParams="java.lang.Object,org.apache.commons.validator.Field"/>
>     </global>
>     <formset>
> -      <form    name="nameForm">
> -         <field    property="firstName"
> -         	   depends="required">
> -         	     <arg0 key="nameForm.firstname.displayname"/>
> -         </field>    
> +     <form name = "baseForm">
>           <field    property="lastName"
>           	   depends="required">
>           	     <arg0 key="nameForm.lastname.displayname"/>
>           </field>
> +     </form>
> +      <form    name="baseOtherForm">
> +         <field    property="firstName1"
> +         	   depends="required">
> +         	     <arg0 key="nameForm.firstname.displayname"/>
> +         </field>    
> +      </form>
> +      <form    name="otherForm" extends="baseOtherForm">
> +      </form>
> +      <form    name="interForm" extends="baseForm">
> +      </form>
> +      <form    name="nameForm"  extends="interForm">
> +         <field property="firstName" uses="otherForm.firstName1"/>
>        </form>
>     </formset>   
>  </form-validation>

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org