[
https://issues.apache.org/jira/browse/VALIDATOR-186?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Sebb resolved VALIDATOR-186.
----------------------------
Resolution: Abandoned
> Enhance the IndexedListProperty to handle nested lists.
> -------------------------------------------------------
>
> Key: VALIDATOR-186
> URL: https://issues.apache.org/jira/browse/VALIDATOR-186
> Project: Commons Validator
> Issue Type: Improvement
> Components: Framework
> Affects Versions: Nightly Builds
> Environment: Operating System: All
> Platform: All
> Reporter: Peter Oldershaw
> Priority: Minor
> Fix For: 2.0
>
>
> Allowing lists to be validated helps a great deal but the code that is
> supplied
> with the commons-validator 1.0 could be simplified and enhanced. There are
> two
> features that it would be nice to have:
> 1) When the IndexedListProperty is used it would be nice if all the fields in
> the list were validated, i.e. the validation did not stop at the first error.
>
> This would need a different loop mechanism and the key to be set to the fully
> qualified path, e.g list[0].value and not list[].value.
> 2) It would be very nice if the IndexedListProperty could be nested, e.g. the
> size for all the doors on all the rooms must be greater than n. I am not
> sure of the syntax in the configuration file that makes sense but offer
> list1.list2.list3. as a suggestion. The PropertyUtils could then be called
> on the bean using the "list1" property to get the first list and then called
> on each of the returned objects using the "list2" property, etc. So, if you
> had two rooms, the first with one door, the second with two and the
> validation.xml Field looked like:
> <field
> property="size"
> indexedListProperty="rooms.doors"
> depends="min">
> <arg0 key="error.door.size"/>
> <var>
> <var-name>min</var-name>
> <var-value>${n}</var-value>
> </var>
> </field>
> The validator waould in effect be called for:
> rooms[0].doors[0].size
> rooms[1].doors[0].size
> rooms[1].doors[1].size
> The code below provides an example of how this could be done. It uses the
> IndexedProperty to pass information down a reentrant stack because I did not
> want to change the parameters to the Validator.validate method but that would
> obviously be safer. Also, by putting this code snipit in place, all the
> other
> isIndexed code could be removed so you don't have to loop twice (as it does
> in
> version 1.0).
> Thanks...Peter
> //////////////////////////////////////////////////////////////////////////////
> // These snippits are out of the Validator class. There is a one line
> // change to validate, one new method and one tidied up method.
> // The code has been tested and works but you may not like the way it
> // changes the BEAN_KEY and uses the IndexProperty.
> //////////////////////////////////////////////////////////////////////////////
> /**
> * Performs validations based on the configured resources.
> * Needed as we cannot access (override) the private methods.
> *
> * @return The <code>Map</code> returned uses the property
> * of the <code>Field</code> for the key and the value
> * is the number of error the field had.
> */
> public ValidatorResults validate() throws ValidatorException
> {
> ValidatorResults results = new ValidatorResults();
> Locale locale = null;
> if (hResources.containsKey(LOCALE_KEY))
> {
> locale = (Locale)hResources.get(LOCALE_KEY);
> }
> hResources.put(VALIDATOR_KEY, this);
> if (locale == null)
> {
> locale = Locale.getDefault();
> }
> Form form = null;
> if (resources == null)
> {
> throw new ValidatorException("Resources not defined for
> Validator");
> }
> if ((form = resources.get(locale, formName)) != null)
> {
> for (Iterator i = form.getFields().iterator(); i.hasNext(); )
> {
> Field field = (Field)i.next();
> if (field.getPage() <= page)
> {
> // *************************************************
> // This is the only line that changed in this method.
> // *************************************************
> validateFieldNested(field, results);
> // *************************************************
> }
> }
> }
> return results;
> }
> /**
> * Validate field nested. This method handles nested list validation
> * of the form IndexedListProperty = list1.list2.list3. It gets all the
> * instances in list1 off the BEAN_KEY (root bean) using the PropertyUtils
> * then gets all the list2 entries of all the list1 objects, then all the
> * list3 objects of the list2 objects, etc. The result is that the
> * validateField method is called on all the list3 objects with the
> property
> * as it was but the BEAN_KEY set to the current list3 object and the
> * Field key set to the fully qualified key (e.g. list1[0].list2[0].list3
> [0]).
> *
> * @param field See Validator.validateField
> * @param allResults See Validator.validateField
> */
> private void validateFieldNested (Field field, ValidatorResults
> allResults)
> throws ValidatorException
> {
> // Does it have an IndexedList property?
> String indexedList = field.getIndexedListProperty();
> if (null != indexedList && 0 < indexedList.length())
> {
> // Is it nested?
> String nestName = indexedList;
> String restName = null;
> int nestOffset = indexedList.indexOf(".");
> if (-1 != nestOffset)
> {
> nestName = indexedList.substring(0, nestOffset);
> restName = indexedList.substring(nestOffset + 1);
> }
> // Build the field object based on the nesting.
> Field indexedField = (Field)field.clone();
> indexedField.setIndexedListProperty(restName);
> // The keyBase is a local copy of the IndexedProperty so that
> // the appropriate instance ([n]) can be added inside the
> // loop. The IndexedProperty is not currently used by the
> // validator framework and so we use it to pass the revised
> // nesting level through the reenterant code.
> String keyBase = field.getIndexedProperty();
> if (null == keyBase)
> {
> keyBase = nestName;
> }
> else
> {
> keyBase += "." + nestName;
> }
> // Save the current BEAN_KEY, the object that the validator
> // is working on, so we can reset it as we pop out of the
> // reenterant loop.
> Object oldBean = hResources.get(BEAN_KEY);
> // Call for each of the objects at this level
> Object[] list = getIndexedList(oldBean, nestName);
> for (int i = 0; i < list.length; i++)
> {
> // Set the BEAN_KEY to the current object
> hResources.put(BEAN_KEY, list[i]);
> // Use the indexedProperty to pass the current level into
> // the next level so that it does not have to do a load of
> // parsing and replacement when it builds the key. The key
> // is used to make the field error (if any) unique.
> indexedField.setIndexedProperty(keyBase + "[" + i + "]");
> indexedField.setKey(indexedField.getIndexedProperty() + "."
> + indexedField.getProperty());
> // Call ourselves to handle the next (if any) level of
> // nesting.
> validateFieldNested(indexedField, allResults);
> }
> // Put the old BEAN_KEY back as we pop the reentrancy stack.
> hResources.put(BEAN_KEY, oldBean);
> return;
> }
> // If it is not nested (actually IndexedList) use the normal method.
> validateField(field, allResults);
> }
> /**
> * Get an array of instances for the supplied property, whether it is a
> * collection or an array. This is just a tidy up of an existing method
> * in the Validator class.
> *
> * @param bean The bean that contains the list or array.
> * @param property The name of the property that will return a list
> * or array on the supplied bean.
> * @return Object[] An array of objects that were retrieved from the
> * supplied property.
> */
> private Object[] getIndexedList(Object bean, String property)
> {
> Object oIndexed;
> try
> {
> oIndexed = PropertyUtils.getProperty(bean, property);
> }
> catch (Exception e)
> {
> log.error("in validateFieldNested", e);
> return null;
> }
> Object indexedList[] = new Object[0];
> if (oIndexed instanceof Collection)
> {
> indexedList = ((Collection)oIndexed).toArray();
> }
> else if (oIndexed.getClass().isArray())
> {
> indexedList = (Object[]) oIndexed;
> }
> return indexedList;
> }
--
This message was sent by Atlassian Jira
(v8.3.4#803005)