View Javadoc

1   package pl.aislib.fm;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.Iterator;
7   import java.util.LinkedHashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.TreeMap;
11  
12  import org.apache.commons.logging.Log;
13  
14  import pl.aislib.fm.forms.Field;
15  import pl.aislib.fm.forms.IEntity;
16  import pl.aislib.fm.forms.ValidateException;
17  import pl.aislib.fm.messages.IMessage;
18  import pl.aislib.fm.messages.IMessageConverter;
19  import pl.aislib.util.Pair;
20  import pl.aislib.util.messages.StandardMessageConverter;
21  
22  /***
23   * Field container class.
24   *
25   * Please note the following:
26   * Messages from forms' validation can be converted using implementation of
27   * <code>IMessageConverter</code> interface. <br>
28   *
29   * One can use <code>MessageFormatConverter</code> to do that. In this case,
30   * message format arguments should be used for each message in the following way:
31   * <ul>
32   *   <li>{1} - name of the entity (field or rule for which the message is generated)</li>
33   *   <li>{2} - name of the dynamic entity (equals to name of the entity for non-dynamic fields or rules)</li>
34   *   <li>{3} - number of the dynamic entity in collection of dynamic fields and rules (starting with 0)</li>
35   *   <li>{4} - number of the dynamic entity in collection of dynamic fields and rules (starting with 1)</li>
36   *   <li>{5} - suffix number of the dynamic entity</li>
37   * </ul>
38   *
39   * @author <a href="mailto:wswiatek@ais.pl">Wojciech Swiatek</a>, AIS.PL
40   * @version $Revision: 1.18 $
41   */
42  public abstract class FieldContainer {
43  
44    // String constants describing things to be put to message converter
45  
46    /*** */
47    public static final String MC_ENTITY_OBJECT = "object";
48  
49    /*** */
50    public static final String MC_ENTITY_NAME = "name";
51  
52    /*** */
53    public static final String MC_ENTITY_GENERIC_NAME = "genericName";
54  
55    /*** */
56    public static final String MC_ENTITY_NAME_SUFFIX = "nameSuffix";
57  
58    /*** */
59    public static final String MC_ENTITY_VALIDATOR = "validator";
60  
61    /*** */
62    public static final String MC_ENTITY_DYNAMIC_COUNT = "dynamicCount";
63  
64    /*** */
65    public static final String MC_ENTITY_DYNAMIC_NUMBER_FROM_0 = "dynamicNumberFrom0";
66  
67    /*** */
68    public static final String MC_ENTITY_DYNAMIC_NUMBER_FROM_1 = "dynamicNumberFrom1";
69  
70    /*** */
71    public static final String MC_ENTITY_VALUE = "value";
72  
73    /*** */
74    public static final String MC_ENTITY_ORIGINAL_VALUE = "originalValue";
75  
76    /*** */
77    public static final String MC_ENTITY_FORM = "form";
78  
79  
80    // Protected fields
81  
82    /***
83     * Name of the container.
84     */
85    protected String name;
86  
87    /***
88     * Map of fields.
89     */
90    protected Map fields;
91  
92    /***
93     * Map of messages (codes and objects).
94     */
95    protected Map messages;
96  
97    /***
98     * Groups of messages (codes and list of messages).
99     */
100   protected Map messageGroups;
101 
102   /***
103    * Map of field values, converted after successful validation.
104    */
105   protected Map values;
106 
107   /***
108    * Map of original field values.
109    */
110   protected Map originalValues;
111 
112   /***
113    * Map of field names and booleans.
114    * Values of the map state whether fields (i.e. keys) have been validated.
115    */
116   protected Map validatedFields;
117 
118   /***
119    * Map of field names and validator objects in case {@link ValidateException} occurs.
120    */
121   protected Map validators;
122 
123   /***
124    * Map of messages from non-validated entities.
125    * Keys are entity names.
126    * Values are messages.
127    */
128   protected Map messagesMap;
129 
130   /***
131    * Map of messages from non-validated entities.
132    * Same as messages map but messages are added in an ordered manner.
133    */
134   protected Map orderedMessagesMap;
135 
136   /***
137    * Language of messages.
138    */
139   protected String lang;
140 
141   /***
142    * Logging object.
143    */
144   protected Log log;
145 
146   /***
147    * True if empty fields should be given null value.
148    */
149   protected boolean bTreatEmptyAsNull = false;
150 
151 
152   // Constructors
153 
154   /***
155    * @param name name of the field.
156    */
157   public FieldContainer(String name) {
158     this.name = name;
159 
160     fields = new LinkedHashMap();
161     messages = new LinkedHashMap();
162     messageGroups = new LinkedHashMap();
163   }
164 
165 
166   // Public methods
167 
168   /***
169    * @param field field object.
170    */
171   public void addField(Field field) {
172     fields.put(field.getName(), field);
173   }
174 
175   /***
176    * @param fieldName name of a field.
177    * @return the field.
178    */
179   public Field getField(String fieldName) {
180     return (Field) fields.get(fieldName);
181   }
182 
183   /***
184    * @return name of the container.
185    */
186   public String getName() {
187     return name;
188   }
189 
190   /***
191    * @param fieldName name of a field.
192    * @return original value of the field. It makes sense only when the field has been tried to be validated.
193    */
194   public String getOriginalValue(String fieldName) {
195     return originalValues != null ? (String) originalValues.get(fieldName) : null;
196   }
197 
198   /***
199    * @return original field values.
200    */
201   public Map getOriginalValues() {
202     return originalValues;
203   }
204 
205   /***
206    * @return map of validated fields.
207    */
208   public Map getValidatedFields() {
209     return validatedFields;
210   }
211 
212   /***
213    * @return map of field names and validators in case {@link ValidateException} occurs.
214    */
215   public Map getValidators() {
216     return validators;
217   }
218 
219   /***
220    * @param fieldName name of a field.
221    * @return value of the field. It makes sense only when the field has been validated.
222    */
223   public Object getValue(String fieldName) {
224     return getValue(fieldName, bTreatEmptyAsNull);
225   }
226 
227   /***
228    *
229    * @param fieldName name of a field.
230    * @param treatEmptyAsNull true if empty field should be given null value.
231    * @return Object
232    */
233   public Object getValue(String fieldName, boolean treatEmptyAsNull) {
234     if (values != null) {
235       Object value = values.get(fieldName);
236       return treatEmptyAsNull && isValueEmpty(value) ? null : value;
237     }
238 
239     return null;
240   }
241 
242   /***
243    * @return map of field values, converted after successful validation.
244    */
245   public Map getValues() {
246     return getValues(bTreatEmptyAsNull);
247   }
248 
249   /***
250    * @param treatEmptyAsNull true if empty fields should be given null value.
251    * @return map of field values.
252    */
253   public Map getValues(boolean treatEmptyAsNull) {
254     if (!treatEmptyAsNull || values == null) {
255       return values;
256     }
257 
258     Map result = new LinkedHashMap(values);
259 
260     for (Iterator i = result.entrySet().iterator(); i.hasNext();) {
261       Map.Entry me = (Map.Entry) i.next();
262       String fieldName = (String) me.getKey();
263       boolean fieldValidated = isFieldValidated(fieldName);
264       if (!fieldValidated || (fieldValidated && isValueEmpty(me.getValue()))) {
265         result.put(fieldName, null);
266       }
267     }
268 
269     return result;
270   }
271 
272   /***
273    *
274    * @param bTreatEmptyAsNull true if empty fields should be given null value.
275    */
276   public void treatEmptyAsNull(boolean bTreatEmptyAsNull) {
277     this.bTreatEmptyAsNull = bTreatEmptyAsNull;
278   }
279 
280   /***
281    *
282    * @param value object to be checked against emptiness
283    * @return true if the object is empty.
284    */
285   protected boolean isValueEmpty(Object value) {
286     if (value == null) {
287       return true;
288     }
289 
290     if (value instanceof String && ((String) value).trim().length() == 0) {
291       return true;
292     }
293 
294     if (value instanceof String[] && ((String[]) value).length == 0) {
295       return true;
296     }
297 
298     return false;
299   }
300 
301   /***
302    * @param fieldName name of a field.
303    * @return true if the field has been validated successfully.
304    */
305   public boolean isFieldValidated(String fieldName) {
306     Boolean b = (Boolean) validatedFields.get(fieldName);
307     return (b != null ? b.booleanValue() : false);
308   }
309 
310   /***
311    * @param log logging object.
312    */
313   public void setLog(Log log) {
314     this.log = log;
315   }
316 
317   /***
318    * Validates fields in the container.
319    *
320    * @param fieldValues map of original string values.
321    * @return true if all fields have been successfully validated.
322    */
323   public boolean validate(Map fieldValues) {
324     return validate(fieldValues, null);
325   }
326 
327   /***
328    * Validates fields in the container.
329    *
330    * @param fieldValues map of original string values.
331    * @param data an object specific for validators.
332    * @return true if all fields have been successfully validated.
333    */
334   public boolean validate(Map fieldValues, Object data) {
335     return preValidate(fieldValues, data)
336          && doValidate(fieldValues, data)
337          && postValidate(fieldValues, data);
338   }
339 
340   /***
341    * Validates fields in the container.
342    *
343    * @param field a field.
344    * @param data an object specific for validators.
345    * @param fieldValues map of original string values.
346    * @return true if the field has been successfully validated.
347    */
348   public boolean validateField(Field field, Object data, Map fieldValues) {
349     String fieldName = field.getName();
350     Object fieldValue = null;
351 
352     if (field.isComplex() && field.isDynamic()) {
353       Map fValues = new LinkedHashMap(fieldValues);
354       Map builderMapping = field.getBuilder().getMapping();
355 
356       //int maxIndex = -1;
357 
358       for (Iterator i = builderMapping.entrySet().iterator(); i.hasNext();) {
359         Map.Entry me = (Map.Entry) i.next();
360         String partFieldName = (String) me.getValue();
361         constrainValues(fValues, partFieldName);
362       }
363 
364       boolean result = true;
365 
366       List l = orderFieldValues(values, builderMapping, fValues);
367       for (Iterator i = l.iterator(); i.hasNext();) {
368         Pair pair = (Pair) i.next();
369         int j = ((Integer) pair.getFirst()).intValue();
370         Map builderValues = (Map) pair.getSecond();
371         if (builderValues != null) {
372           fieldValue = field.getBuilder().join(builderValues);
373           result &= validateField(field, field.getName() + j, data, fieldValue);
374         }
375       }
376 
377       return result;
378     } else if (field.isComplex()) {
379       Map builderValues = constrainValues(values, field.getBuilder().getMapping(), true);
380       if (builderValues == null) {
381         // No validation is performed, because null is returned when
382         // at least one of base fields has not been validated
383         return true;
384       }
385       fieldValue = field.getBuilder().join(builderValues);
386     } else if (field.isDynamic()) {
387       Map fValues = new LinkedHashMap(fieldValues);
388       Map constrainedValues = constrainValues(fValues, field.getName());
389       boolean result = true;
390       for (Iterator i = constrainedValues.entrySet().iterator(); i.hasNext();) {
391         Map.Entry me = (Map.Entry) i.next();
392         result &= validateField(field, (String) me.getKey(), data, me.getValue());
393       }
394       return result;
395     } else {
396       fieldValue = fieldValues.get(fieldName);
397     }
398 
399     return validateField(field, data, fieldValue);
400   }
401 
402   /***
403    * Validates a single field in the container.
404    *
405    * @param field a field.
406    * @param data an object specific for validators.
407    * @param fieldValue value of the field.
408    * @return true if the field has been successfully validated.
409    */
410   public boolean validateField(Field field, Object data, Object fieldValue) {
411     return validateField(field, field.getName(), data, fieldValue);
412   }
413 
414   /***
415    * Validates a single field in the container.
416    *
417    * @param field a field.
418    * @param fieldName name of the field (used primarily for dynamic fields).
419    * @param data an object specific for validators.
420    * @param fieldValue value of the field.
421    * @return true if the field has been successfully validated.
422    */
423   public boolean validateField(Field field, String fieldName, Object data, Object fieldValue) {
424     stamp("Validating field: " + fieldName + " ...");
425 
426     int valuesType = field.getValuesType();
427 
428     if (fieldValue == null) {
429       if (valuesType == Field.VT_SINGLE) {
430         fieldValue = new String();
431       } else {
432         fieldValue = new String[0];
433       }
434     }
435 
436     Object theValue = null;
437     try {
438       originalValues.put(fieldName, fieldValue);
439       validatedFields.put(fieldName, Boolean.TRUE);
440       switch (valuesType) {
441         case Field.VT_SINGLE:
442           if (fieldValue instanceof String) {
443             theValue = field.validate((String) fieldValue, messagesMap, fieldName, data);
444             break;
445           } else if (fieldValue instanceof String[]) {
446             String[] arr = (String[]) fieldValue;
447             originalValues.put(fieldName, arr[0]);
448             theValue = field.validate(arr[0], messagesMap, fieldName, data);
449             break;
450           } else {
451             validatedFields.put(fieldName, Boolean.FALSE);
452             return false;
453           }
454         case Field.VT_ARRAY:
455           if (fieldValue instanceof String) {
456             String[] arr = new String[1];
457             arr[0] = (String) fieldValue;
458             originalValues.put(fieldName, arr);
459             theValue = field.validate(arr, messagesMap, fieldName, data);
460             break;
461           } else if (fieldValue instanceof String[]) {
462             theValue = field.validate((String[]) fieldValue, messagesMap, fieldName, data);
463             break;
464           } else {
465             validatedFields.put(fieldName, Boolean.FALSE);
466             return false;
467           }
468         default :
469           if (fieldValue instanceof String) {
470             theValue = fieldValue;
471           } else if (fieldValue instanceof String[]) {
472             String[] aValue = (String[]) fieldValue;
473             if (aValue.length == 1) {
474               theValue = aValue[0];
475             } else {
476               theValue = aValue;
477             }
478           } else {
479             validatedFields.put(fieldName, Boolean.FALSE);
480             return false;
481           }
482       }
483       values.put(fieldName, isValueEmpty(theValue) ? null : theValue);
484       return true;
485     } catch (ValidateException ve) {
486       Iterator keys = messagesMap.values().iterator();
487       Object last = null;
488       while (keys.hasNext()) {
489         last = keys.next();
490       }
491       orderedMessagesMap.put(fieldName, last);
492       validatedFields.put(fieldName, Boolean.FALSE);
493       validators.put(fieldName, ve.getValidator());
494       stamp("Field '" + fieldName + "' has not been successfully validated. Returned message: " + ve.getMessage());
495       return false;
496     }
497   }
498 
499 
500   // Protected methods
501 
502   /***
503    * Contains all necessary steps taken before the proper validation of fields in the container.
504    *
505    * @param fieldValues map of field values.
506    * @param data an object specific for validators.
507    * @return true if all fields have been successfully pre-validated.
508    */
509   protected boolean preValidate(Map fieldValues, Object data) {
510     values = new LinkedHashMap();
511     originalValues = new LinkedHashMap();
512     validatedFields = new LinkedHashMap();
513     validators = new LinkedHashMap();
514     messagesMap = new LinkedHashMap();
515     orderedMessagesMap = new LinkedHashMap();
516     return true;
517   }
518 
519   /***
520    * Main step of validation of fields in the container.
521    *
522    * @param fieldValues map of field values.
523    * @param data an object specific for validators.
524    * @return true if all fields have been successfully validated.
525    */
526   protected abstract boolean doValidate(Map fieldValues, Object data);
527 
528   /***
529    * Contains all necessary steps taken after the proper validation of fields in the container.
530    * @param fieldValues map of field values.
531    * @param data an object specific for validators.
532    * @return true if all fields have been successfully post-validated.
533    */
534   protected boolean postValidate(Map fieldValues, Object data) {
535     return true;
536   }
537 
538   /***
539    * @param fieldValues map of field values, as a reference.
540    * @param fieldName name of a field.
541    * @return map of values of fields that start with the given name.
542    */
543   protected Map constrainValues(Map fieldValues, String fieldName) {
544     Map result = new TreeMap();
545 
546     int maxIndex = -1;
547     try {
548       maxIndex = Integer.parseInt((String) fieldValues.get(fieldName + "_count"));
549     } catch (Exception e) {
550       ;
551     }
552 
553     for (Iterator i = fieldValues.entrySet().iterator(); i.hasNext();) {
554       Map.Entry me = (Map.Entry) i.next();
555       String key = (String) me.getKey();
556       if (key.startsWith(fieldName)) {
557         try {
558           int index = Integer.parseInt(key.substring(fieldName.length()));
559           if (index > maxIndex) {
560             maxIndex = index;
561           }
562           result.put(key, me.getValue());
563         } catch (Exception e) {
564           ;
565         }
566       }
567     }
568 
569     for (int i = 1; i <= maxIndex; i++) {
570       if (!fieldValues.containsKey(fieldName + i)) {
571         result.put(fieldName + i, null);
572       }
573     }
574 
575     return new LinkedHashMap(result);
576   }
577 
578   /***
579    * Method constrainValues.
580    * @param values map of values.
581    * @param mapping a map.
582    * @param checkValidation true if values should be checked against having been validated.
583    * @return map of constrained values.
584    */
585   protected Map constrainValues(Map values, Map mapping, boolean checkValidation) {
586     Map result = new LinkedHashMap();
587     for (Iterator i = mapping.entrySet().iterator(); i.hasNext();) {
588       Map.Entry me = (Map.Entry) i.next();
589       String value = (String) me.getValue();
590       if (checkValidation && !isFieldValidated(value)) {
591         return null;
592       }
593       result.put(me.getKey(), values.get(value));
594     }
595     return result;
596   }
597 
598   /***
599    * @param fieldValues map of field values.
600    * @param data an object specific for validators.
601    * @return true if all complex fields have been successfully validated.
602    */
603   protected boolean validateComplexFields(Map fieldValues, Object data) {
604     boolean result = true;
605 
606     for (Iterator i = fields.entrySet().iterator(); i.hasNext();) {
607       Field field = (Field) ((Map.Entry) i.next()).getValue();
608       if (field.isConditional() || !field.isComplex()) {
609         continue;
610       }
611 
612       boolean bFieldValidated = validateField(field, data, fieldValues);
613 
614       validatedFields.put(field.getName(), new Boolean(bFieldValidated));
615       result &= bFieldValidated;
616     }
617 
618     return result;
619   }
620 
621   /***
622    * @param fieldValues map of field values.
623    * @param data an object specific for validators.
624    * @return true if all non-complex fields have been successfully validated.
625    */
626   protected boolean validateNonComplexFields(Map fieldValues, Object data) {
627     boolean result = true;
628 
629     for (Iterator i = fields.entrySet().iterator(); i.hasNext();) {
630       Field field = (Field) ((Map.Entry) i.next()).getValue();
631       if (field.isConditional() || field.isComplex()) {
632         continue;
633       }
634 
635       boolean bFieldValidated = validateField(field, data, fieldValues);
636 
637       validatedFields.put(field.getName(), new Boolean(bFieldValidated));
638       result &= bFieldValidated;
639     }
640 
641     return result;
642   }
643 
644   /***
645    * @param str log message.
646    */
647   protected void stamp(String str) {
648     if (log != null && log.isDebugEnabled()) {
649       log.debug(str);
650     }
651   }
652 
653 
654   // wswiatek: I would like to make those methods deprecated
655 
656   /***
657    * @return iterator of message codes.
658    */
659   public Iterator getErrorCodes() {
660     return getMessageCodes();
661   }
662 
663   /***
664    * @param fieldName name of a field.
665    * @return message for the field.
666    */
667   public IMessage getErrorMessage(String fieldName) {
668     return (IMessage) messages.get(messagesMap.get(fieldName));
669   }
670 
671   /***
672    * @return map of messages.
673    */
674   public Map getErrorMessages() {
675     return getErrorMessages(null);
676   }
677 
678   public Map getErrorMessages(IMessageConverter messageConverter) {
679     return getErrorMessages(messageConverter, null);
680   }
681 
682   /***
683    * This method should be called when one wants to use dynamic fields or rules.
684    *
685    * Two items are put into message converter values: the whole entity and a real entity name.
686    * Same objects go as message converter keys.
687    *
688    * @param messageConverter message converter object.
689    * @return map of messages with converted key and content.
690    */
691   protected Map getErrorMessages(IMessageConverter messageConverter, Map formEntityProperties) {
692     Map result = new LinkedHashMap();
693 
694     if (formEntityProperties == null) {
695       formEntityProperties = getEntitiesProperties();
696     }
697 
698     for (Iterator i = fields.keySet().iterator(); i.hasNext();) {
699       String fieldName = (String) i.next();
700       result.putAll(getEntityMessages(fieldName, messageConverter, formEntityProperties));
701     }
702 
703     return result;
704   }
705 
706   protected Map getEntitiesProperties() {
707     Map result = new HashMap();
708 
709     for (Iterator iter = fields.keySet().iterator(); iter.hasNext(); ) {
710       String fieldName = (String) iter.next();
711       getEntityProperties(fieldName, result);
712     }
713 
714     return result;
715   }
716 
717   protected void getEntityProperties(String entityName, Map formEntityProperties) {
718     IEntity entity = getEntity(entityName);
719     if (entity == null) {
720       return;
721     }
722 
723     List entities = null;
724     if (entity.isDynamic()) {
725       entities = getDynamicEntityNamesList(entityName);
726     } else {
727       entities = new ArrayList();
728       entities.add(entityName);
729     }
730 
731     Integer entitiesCount = new Integer(entities.size());
732 
733     int counter = 0;
734 
735     for (Iterator i = entities.iterator(); i.hasNext(); counter++) {
736       String entName = (String) i.next();
737 
738       Map entityProperties = new LinkedHashMap();
739 
740       entityProperties.put(MC_ENTITY_OBJECT, entity);
741       entityProperties.put(MC_ENTITY_GENERIC_NAME, entityName);
742 
743       if (entity.isDynamic()) {
744         entityProperties.put(MC_ENTITY_DYNAMIC_COUNT, entitiesCount);
745       }
746 
747       entityProperties.put(MC_ENTITY_DYNAMIC_NUMBER_FROM_0, new Integer(counter));
748       entityProperties.put(MC_ENTITY_DYNAMIC_NUMBER_FROM_1, new Integer(counter + 1));
749 
750       addSpecificEntityProperties(entity, entName, entityProperties);
751 
752       formEntityProperties.put(entName, entityProperties);
753     }
754   }
755 
756   /***
757    * @param entityName name of a field or a rule.
758    * @param messageConverter message converter object.
759    * @return map of messages for given entity (the entity may be dynamic).
760    */
761   protected Map getEntityMessages(String entityName, IMessageConverter messageConverter, Map formEntityProperties) {
762     if (messageConverter == null) {
763       messageConverter = new StandardMessageConverter();
764     }
765 
766     Map result = new LinkedHashMap();
767 
768     IEntity entity = getEntity(entityName);
769 
770     if (entity == null) {
771       return result;
772     }
773 
774     List entities = null;
775     if (entity.isDynamic()) {
776       entities = getDynamicEntityNamesList(entityName);
777     } else {
778       entities = new ArrayList();
779       entities.add(entityName);
780     }
781 
782     int counter = 0;
783     for (Iterator i = entities.iterator(); i.hasNext(); counter++) {
784       String entName = (String) i.next();
785       Integer errorCode = (Integer) orderedMessagesMap.get(entName);
786       Message message = (Message) messages.get(errorCode);
787 
788       Map entityProperties = (Map) formEntityProperties.get(entName);
789       entityProperties.put(MC_ENTITY_FORM, formEntityProperties);
790 
791       // First, check not grouped messages
792       if (message != null) {
793         IMessage cMessage = messageConverter.convert(message, lang, entityProperties, entityProperties);
794         result.put(cMessage.getKey(), cMessage.getContent());
795       } else {
796         // Then, check grouped messages
797         List messageGroup = (List) messageGroups.get(errorCode);
798         if (messageGroup != null) {
799           for (Iterator j = messageGroup.iterator(); j.hasNext();) {
800             Message gMessage = (Message) j.next();
801             if (gMessage != null) {
802               IMessage cMessage = messageConverter.convert(gMessage, lang, entityProperties, entityProperties);
803               result.put(cMessage.getKey(), cMessage.getContent());
804             }
805           }
806         }
807       }
808     }
809 
810     return result;
811   }
812 
813   /***
814    * Adds specific entity properties to given map.
815    *
816    * @param entity <code>IEntity</code> object.
817    * @param entityName name of entity.
818    * @param entityProperties not nullable <code>Map</code> of entity properties.
819    */
820   protected void addSpecificEntityProperties(IEntity entity, String entityName, Map entityProperties) {
821     entityProperties.put(MC_ENTITY_NAME, entityName);
822     entityProperties.put(MC_ENTITY_NAME_SUFFIX, entityName.substring(entityName.lastIndexOf("_") + 1));
823 
824     if (entity instanceof Field) {
825       entityProperties.put(MC_ENTITY_VALIDATOR, validators.get(entityName));
826       entityProperties.put(MC_ENTITY_VALUE, values.get(entityName));
827       entityProperties.put(MC_ENTITY_ORIGINAL_VALUE, originalValues.get(entityName));
828     }
829   }
830 
831   /***
832    * @param entityName name of an entity.
833    * @return the entity.
834    */
835   protected IEntity getEntity(String entityName) {
836     return (IEntity) fields.get(entityName);
837   }
838 
839   /***
840    * @param entityName name of a dynamic entity.
841    * @return list of all names of entity's instances.
842    */
843   protected List getDynamicEntityNamesList(String entityName) {
844     List result = new ArrayList();
845 
846     IEntity entity = getEntity(entityName);
847 
848     for (Iterator i = validatedFields.keySet().iterator(); i.hasNext();) {
849       String fieldName = (String) i.next();
850       if (fieldName.startsWith(entityName)) {
851         if (!entity.isDynamic()) {
852           result.add(fieldName);
853         } else {
854           try {
855             Integer.parseInt(fieldName.substring(fieldName.lastIndexOf("_") + 1));
856             result.add(fieldName);
857           } catch (Exception ex) {
858             ;
859           }
860         }
861       }
862     }
863 
864     return result;
865   }
866 
867   /***
868    * @param entityName name of a dynamic entity.
869    * @return iterator for all names of entity's instances.
870    */
871   protected Iterator getDynamicEntityNames(String entityName) {
872     return getDynamicEntityNamesList(entityName).iterator();
873   }
874 
875   /***
876    * @return iterator of field names.
877    */
878   public Iterator getFieldNames() {
879     return fields.keySet().iterator();
880   }
881 
882   /***
883    * @param fieldName of a field.
884    * @return message code for the field.
885    */
886   public int getMessageCode(String fieldName) {
887     return ((Integer) messagesMap.get(fieldName)).intValue();
888   }
889 
890   /***
891    * @return iterator of message codes.
892    */
893   public Iterator getMessageCodes() {
894     return orderedMessagesMap.values().iterator();
895   }
896 
897   /***
898    * @return map of original values.
899    */
900   public Map getStringValues() {
901     return getOriginalValues();
902   }
903 
904   /***
905    * @param fieldValues string values of fields.
906    * @param fieldName name of a field.
907    * @return list of consecutive numbers of entity's instances.
908    */
909   protected List getDynamicNumbers(Map fieldValues, String fieldName) {
910     List result = new ArrayList();
911     for (Iterator i = fieldValues.entrySet().iterator(); i.hasNext();) {
912       Map.Entry me = (Map.Entry) i.next();
913       String key = (String) me.getKey();
914       if (key.startsWith(fieldName)) {
915         try {
916           Integer j = Integer.valueOf(key.substring(fieldName.length()));
917           result.add(j);
918         } catch (Exception e) {
919           ;
920         }
921       }
922     }
923     Collections.sort(result);
924     return result;
925   }
926 
927   /***
928    * @param values values of fields.
929    * @param mapping mapping.
930    * @param fieldValues string values of fields.
931    * @return list of pairs: number and map of values of fields for entity's instance.
932    */
933   protected List orderFieldValues(Map values, Map mapping, Map fieldValues) {
934     List result = new ArrayList();
935 
936     List l = new ArrayList();
937     boolean bDynamicFound = false;
938     boolean bNoDynamicFound = true;
939     for (Iterator i = mapping.entrySet().iterator(); i.hasNext();) {
940       Map.Entry me = (Map.Entry) i.next();
941       String fieldName = (String) me.getValue();
942       Field field = (Field) fields.get(fieldName);
943       if (field.isDynamic()) {
944         bNoDynamicFound = false;
945         if (field.isConditional() && field.isComplex()) {
946           for (Iterator j = field.getBuilder().getMapping().entrySet().iterator(); j.hasNext();) {
947             Map.Entry me2 = (Map.Entry) j.next();
948             String partFieldName = (String) me2.getValue();
949             //Field partField = (Field) fields.get(partFieldName);
950             if (!bDynamicFound) {
951               l = getDynamicNumbers(fieldValues, partFieldName);
952               bDynamicFound = true;
953             } else {
954               l.retainAll(getDynamicNumbers(fieldValues, partFieldName));
955             }
956           }
957         } else {
958           if (!bDynamicFound) {
959             l = getDynamicNumbers(
960                   (isFieldValidated(fieldName) || fieldValues == null) ? values : fieldValues,
961                   fieldName
962                 );
963             bDynamicFound = true;
964           } else {
965             l.retainAll(
966               getDynamicNumbers(
967                 (isFieldValidated(fieldName) || fieldValues == null) ? values : fieldValues,
968                 fieldName
969               )
970             );
971           }
972         }
973       }
974     }
975 
976     if (bNoDynamicFound) {
977       l.add(new Integer(-1));
978     }
979 
980     for (Iterator i = l.iterator(); i.hasNext();) {
981       int j = ((Integer) i.next()).intValue();
982       Map map = new HashMap();
983       for (Iterator k = mapping.entrySet().iterator(); k.hasNext();) {
984         Map.Entry me = (Map.Entry) k.next();
985         String fieldName = (String) me.getValue();
986         Field field = (Field) fields.get(fieldName);
987         if (field.isDynamic()) {
988           if (isFieldValidated(fieldName + j) || fieldValues == null) {
989             map.put(me.getKey(), values.get(fieldName + j));
990           } else {
991             map.put(me.getKey(), fieldValues.get(fieldName + j));
992           }
993         } else {
994           if (isFieldValidated(fieldName) || fieldValues == null) {
995             map.put(me.getKey(), values.get(fieldName));
996           } else {
997             map.put(me.getKey(), fieldValues.get(fieldName));
998           }
999         }
1000       }
1001       result.add(new Pair(new Integer(j), map));
1002     }
1003 
1004     return result;
1005   }
1006 
1007 } // FieldContainer class