Details
-
Improvement
-
Status: Closed
-
Minor
-
Resolution: Duplicate
-
None
-
None
-
None
-
Operating System: other
Platform: Other
-
13612
Description
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)
+
+ /**
+ * Returns the name of the field to use.
+ * of this field.
+ * @return String
+ */
+ public String getUses()
+
+ /**
+ * 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)
+
/**
- 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 @@
- src/share/org/apache/commons/validator/Form.java 30 Mar 2002 04:33:17 -
-
- 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("\n");
for (Iterator i = lFields.iterator(); i.hasNext(); )
{ @@ -172,4 +182,21 @@ return results.toString(); }+ /**
+ * Returns the extended form.
+ * @return String
+ */
+ public String getExtends()
+
+ /**
+ * 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)
+
}
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(); )Unknown macro: {+ 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)Unknown macro: {+ 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)
+
+ /**
+ * 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)
+ mergeForms(fs, currentForm, origForm, newForm);
+ if (currentForm.getExtends() != null) {
+ Form extendedForm = get(fs.getCountry(), fs.getLanguage(),
+ fs.getVariant(), currentForm.getExtends());
+ if (extendedForm != null)
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))
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)
+
+ /**
+ * 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)
+ 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)
+
+ if (usedField.getUses() != null)
+ 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>
Attachments
Issue Links
- duplicates
-
VALIDATOR-132 validator & inheritance
- Closed