View Javadoc

1   /*
2    * Copyright 2005-2009 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kns.util;
17  
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.commons.lang.StringEscapeUtils;
29  import org.apache.commons.lang.StringUtils;
30  import org.kuali.rice.kns.util.KNSConstants;
31  
32  
33  /**
34   * Holds errors due to validation. Keys of map represent property paths, and value is a TypedArrayList that contains resource string
35   * keys (to retrieve the error message).
36   *
37   * Note, prior to rice 0.9.4, this class implemented {@link java.util.Map}.  The implements has been removed as of rice 0.9.4
38   */
39  public class MessageMap implements Serializable {
40      private static final long serialVersionUID = -2328635367656516150L;
41      private List<String> errorPath = new ArrayList<String>();
42      private Map<String, TypedArrayList> errorMessages = new LinkedHashMap<String, TypedArrayList>();
43      private Map<String, TypedArrayList> warningMessages = new LinkedHashMap<String, TypedArrayList>();
44      private Map<String, TypedArrayList> infoMessages = new LinkedHashMap<String, TypedArrayList>();
45  
46      public MessageMap() {}
47  
48      public MessageMap(MessageMap messageMap) {
49      	this.errorPath = messageMap.errorPath;
50      	this.errorMessages = messageMap.errorMessages;
51      	this.warningMessages = messageMap.warningMessages;
52      	this.infoMessages = messageMap.infoMessages;
53      }
54  
55  
56      public void merge(MessageMap messageMap){
57      	if(messageMap != null){
58      		if(messageMap.hasErrors()){
59      			merge(messageMap.getErrorMessages(), errorMessages);
60      		}
61      		if(messageMap.hasInfo()){
62      			merge(messageMap.getInfoMessages(), infoMessages);
63      		}
64      		if(messageMap.hasWarnings()){
65      			merge(messageMap.getWarningMessages(), warningMessages);
66      		}
67  
68      	}
69  
70      }
71  
72      /**
73       *
74       * This method takes one message map and merges it into another.  Makes sure there are no duplicates.
75       *
76       * @param messagesFrom
77       * @param messagesTo
78       */
79      protected void merge(Map<String, TypedArrayList> messagesFrom, Map<String, TypedArrayList> messagesTo){
80      	for(String key : messagesFrom.keySet()){
81  
82      		if(messagesTo.containsKey(key)){
83      			// now we need to merge the messages
84      			TypedArrayList tal = messagesFrom.get(key);
85      			TypedArrayList parentList = messagesTo.get(key);
86  
87      			for(Object o :tal){
88  
89      				 if ( !parentList.contains( (ErrorMessage)o ) ) {
90      					 parentList.add((ErrorMessage)o);
91      			     }
92      			}
93  
94      		}else{
95      			messagesTo.put(key, messagesFrom.get(key));
96      		}
97  
98      	}
99  
100     }
101     /**
102      * Adds an error to the map under the given propertyName and adds an array of message parameters. This will fully prepend the
103      * error key with any value in the errorPath list. This should be used when you do not want to add the error with the prepend
104      * pre-built error path.
105      *
106      * @param propertyName name of the property to add error under
107      * @param errorKey resource key used to retrieve the error text from the error message resource bundle
108      * @param errorParameters zero or more string parameters for the displayed error message
109      * @return TypedArrayList
110      */
111     public TypedArrayList putError(String propertyName, String errorKey, String... errorParameters) {
112         return putMessageInMap(errorMessages, propertyName, errorKey, true, true, errorParameters);
113     }
114 
115     public TypedArrayList putWarning(String propertyName, String messageKey, String... messageParameters) {
116         return putMessageInMap(warningMessages, propertyName, messageKey, true, true, messageParameters);
117     }
118 
119     public TypedArrayList putInfo(String propertyName, String messageKey, String... messageParameters) {
120         return putMessageInMap(infoMessages, propertyName, messageKey, true, true, messageParameters);
121     }
122 
123     /**
124      * Adds an error to the map under the given propertyName and adds an array of message parameters. This will fully prepend the
125      * error key with any value in the errorPath list.
126      *
127      * @param propertyName name of the property to add error under
128      * @param errorKey resource key used to retrieve the error text from the error message resource bundle
129      * @param errorParameters zero or more string parameters for the displayed error message
130      * @return TypedArrayList
131      */
132     public TypedArrayList putErrorWithoutFullErrorPath(String propertyName, String errorKey, String... errorParameters) {
133         return putMessageInMap(errorMessages, propertyName, errorKey, false, true, errorParameters);
134     }
135 
136     public TypedArrayList putWarningWithoutFullErrorPath(String propertyName, String messageKey, String... messageParameters) {
137         return putMessageInMap(warningMessages, propertyName, messageKey, false, true, messageParameters);
138     }
139 
140     public TypedArrayList putInfoWithoutFullErrorPath(String propertyName, String messageKey, String... messageParameters) {
141         return putMessageInMap(infoMessages, propertyName, messageKey, false, true, messageParameters);
142     }
143 
144     /**
145      * Adds an error related to a particular section identified by its section ID.  For maintenance documents, the section ID is identified
146      * by calling {@link org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition#getId()}
147      *
148      * @param sectionId
149      * @param errorKey
150      * @param errorParameters
151      * @return
152      */
153     public TypedArrayList putErrorForSectionId(String sectionId, String errorKey, String... errorParameters) {
154     	return putErrorWithoutFullErrorPath(sectionId, errorKey, errorParameters);
155     }
156 
157     public TypedArrayList putWarningForSectionId(String sectionId, String messageKey, String... messageParameters) {
158     	return putWarningWithoutFullErrorPath(sectionId, messageKey, messageParameters);
159     }
160 
161     public TypedArrayList putInfoForSectionId(String sectionId, String messageKey, String... messageParameters) {
162     	return putInfoWithoutFullErrorPath(sectionId, messageKey, messageParameters);
163     }
164 
165     /**
166      * adds an error to the map under the given propertyName and adds an array of message parameters.
167      *
168      * @param propertyName name of the property to add error under
169      * @param messageKey resource key used to retrieve the error text from the error message resource bundle
170      * @param withFullErrorPath true if you want the whole parent error path appended, false otherwise
171      * @param escapeHtmlMessageParameters whether to escape HTML characters in the message parameters, provides protection against XSS attacks
172      * @param messageParameters zero or more string parameters for the displayed error message
173      * @return TypeArrayList
174      */
175     private TypedArrayList putMessageInMap(Map<String, TypedArrayList> messagesMap, String propertyName, String messageKey, boolean withFullErrorPath, boolean escapeHtmlMessageParameters, String... messageParameters) {
176         if (StringUtils.isBlank(propertyName)) {
177             throw new IllegalArgumentException("invalid (blank) propertyName");
178         }
179         if (StringUtils.isBlank(messageKey)) {
180             throw new IllegalArgumentException("invalid (blank) errorKey");
181         }
182 
183         // check if we have previous errors for this property
184         TypedArrayList errorList = null;
185         String propertyKey = getKeyPath((String) propertyName, withFullErrorPath);
186         if (messagesMap.containsKey(propertyKey)) {
187             errorList = (TypedArrayList) messagesMap.get(propertyKey);
188         }
189         else {
190             errorList = new TypedArrayList(ErrorMessage.class);
191         }
192 
193         if (escapeHtmlMessageParameters && messageParameters != null) {
194         	String[] filteredMessageParameters = new String[messageParameters.length];
195         	for (int i = 0; i < messageParameters.length; i++) {
196         		filteredMessageParameters[i] = StringEscapeUtils.escapeHtml(messageParameters[i]);
197         	}
198         	messageParameters = filteredMessageParameters;
199         }
200 
201         // add error to list
202         ErrorMessage errorMessage = new ErrorMessage(messageKey, messageParameters);
203         // check if this error has already been added to the list
204         if ( !errorList.contains( errorMessage ) ) {
205             errorList.add(errorMessage);
206         }
207 
208         return (TypedArrayList) messagesMap.put(propertyKey, errorList);
209     }
210 
211     /**
212      * If any error messages with the key targetKey exist in this ErrorMap for the named property, those ErrorMessages will be
213      * replaced with a new ErrorMessage with the given replaceKey and replaceParameters.
214      *
215      * @param propertyName name of the property where existing error will be replaced
216      * @param targetKey error key of message to be replaced
217      * @paran replaceKey error key which will replace targetKey
218      * @param replaceParameters zero or more string parameters for the replacement error message
219      * @return true if the replacement occurred
220      */
221     public boolean replaceError(String propertyName, String targetKey, String replaceKey, String... replaceParameters) {
222         return replaceError(propertyName, targetKey, true, replaceKey, replaceParameters);
223     }
224 
225     /**
226      * If any error messages with the key targetKey exist in this ErrorMap for the named property, those ErrorMessages will be
227      * replaced with a new ErrorMessage with the given replaceKey and replaceParameters. The targetKey and replaceKey will be
228      * prepended with the current errorPath, if any.
229      *
230      *
231      * @param propertyName name of the property where existing error will be replaced
232      * @param targetKey error key of message to be replaced
233      * @paran replaceKey error key which will replace targetKey
234      * @param replaceParameters zero or more string parameters for the replacement error message
235      * @return true if the replacement occurred
236      */
237     public boolean replaceErrorWithoutFullErrorPath(String propertyName, String targetKey, String replaceKey, String... replaceParameters) {
238         return replaceError(propertyName, targetKey, false, replaceKey, replaceParameters);
239     }
240 
241 
242     /**
243      * If any error messages with the key targetKey exist in this ErrorMap for the named property, those ErrorMessages will be
244      * replaced with a new ErrorMessage with the given replaceKey and replaceParameters.
245      *
246      * @param propertyName name of the property to add error under
247      * @param errorKey resource key used to retrieve the error text
248      * @param withFullErrorPath true if you want the whole parent error path appended, false otherwise
249      * @param errorParameters zero or more string parameters for the displayed error message
250      * @return true if the replacement occurred
251      */
252     private boolean replaceError(String propertyName, String targetKey, boolean withFullErrorPath, String replaceKey, String... replaceParameters) {
253         boolean replaced = false;
254 
255         if (StringUtils.isBlank(propertyName)) {
256             throw new IllegalArgumentException("invalid (blank) propertyName");
257         }
258         if (StringUtils.isBlank(targetKey)) {
259             throw new IllegalArgumentException("invalid (blank) targetKey");
260         }
261         if (StringUtils.isBlank(replaceKey)) {
262             throw new IllegalArgumentException("invalid (blank) replaceKey");
263         }
264 
265         // check if we have previous errors for this property
266         TypedArrayList errorList = null;
267         String propertyKey = getKeyPath((String) propertyName, withFullErrorPath);
268         if (errorMessages.containsKey(propertyKey)) {
269             errorList = (TypedArrayList) errorMessages.get(propertyKey);
270 
271             // look for the specific targetKey
272             for (int i = 0; i < errorList.size(); ++i) {
273                 ErrorMessage em = (ErrorMessage) errorList.get(i);
274 
275                 // replace matching messages
276                 if (em.getErrorKey().equals(targetKey)) {
277                     ErrorMessage rm = new ErrorMessage(replaceKey, replaceParameters);
278                     errorList.set(i, rm);
279                     replaced = true;
280                 }
281             }
282         }
283 
284         return replaced;
285     }
286 
287 
288     /**
289      * Returns true if the named field has a message with the given errorKey
290      *
291      * @param errorKey
292      * @param fieldName
293      * @return boolean
294      */
295     public boolean fieldHasMessage(String fieldName, String errorKey) {
296         boolean found = false;
297 
298         List fieldMessages = (List) errorMessages.get(fieldName);
299         if (fieldMessages != null) {
300             for (Iterator i = fieldMessages.iterator(); !found && i.hasNext();) {
301                 ErrorMessage errorMessage = (ErrorMessage) i.next();
302                 found = errorMessage.getErrorKey().equals(errorKey);
303             }
304         }
305 
306         return found;
307     }
308 
309     /**
310      * Returns the number of messages for the given field
311      *
312      * @param fieldName
313      * @return int
314      */
315     public int countFieldMessages(String fieldName) {
316         int count = 0;
317 
318         List fieldMessages = (List) errorMessages.get(fieldName);
319         if (fieldMessages != null) {
320             count = fieldMessages.size();
321         }
322 
323         return count;
324     }
325 
326 
327     /**
328      * @return true if the given messageKey is associated with some property in this ErrorMap
329      */
330     public boolean containsMessageKey(String messageKey) {
331         ErrorMessage foundMessage = null;
332 
333         if (!isEmpty()) {
334             for (Iterator i = entrySet().iterator(); (foundMessage == null) && i.hasNext();) {
335                 Map.Entry e = (Map.Entry) i.next();
336                 String entryKey = (String) e.getKey();
337                 TypedArrayList entryErrorList = (TypedArrayList) e.getValue();
338                 for (Iterator j = entryErrorList.iterator(); j.hasNext();) {
339                     ErrorMessage em = (ErrorMessage) j.next();
340                     if (messageKey.equals(em.getErrorKey())) {
341                         foundMessage = em;
342                     }
343                 }
344             }
345         }
346 
347         return (foundMessage != null);
348     }
349 
350 
351     private int getMessageCount(Map<String, TypedArrayList> messageMap) {
352         int messageCount = 0;
353         for (Iterator iter = messageMap.keySet().iterator(); iter.hasNext();) {
354             String errorKey = (String) iter.next();
355             List errors = (List) messageMap.get(errorKey);
356             messageCount += errors.size();
357         }
358 
359         return messageCount;
360     }
361 
362     /**
363      * Counts the total number of error messages in the map
364      *
365      * @return returns an int for the total number of errors
366      */
367     public int getErrorCount() {
368     	return getMessageCount(errorMessages);
369     }
370 
371     /**
372      * Counts the total number of warning messages in the map
373      *
374      * @return returns an int for the total number of warnings
375      */
376     public int getWarningCount() {
377     	return getMessageCount(warningMessages);
378     }
379 
380     /**
381      * Counts the total number of info messages in the map
382      *
383      * @return returns an int for the total number of info
384      */
385     public int getInfoCount() {
386     	return getMessageCount(infoMessages);
387     }
388 
389     /**
390      * @param path
391      * @return Returns a List of ErrorMessages for the given path
392      */
393     public TypedArrayList getMessages(String path) {
394         return (TypedArrayList) errorMessages.get(path);
395     }
396 
397     /**
398      * Adds a string prefix to the error path.
399      *
400      * @param parentName
401      */
402     public void addToErrorPath(String parentName) {
403         errorPath.add(parentName);
404     }
405 
406     /**
407      * This method returns the list that holds the error path values.
408      *
409      * @return List
410      */
411     public List<String> getErrorPath() {
412         return errorPath;
413     }
414 
415     /**
416      * Removes a string prefix from the error path.
417      *
418      * @param parentName
419      * @return boolean Returns true if the parentName existed, false otherwise.
420      */
421     public boolean removeFromErrorPath(String parentName) {
422         return errorPath.remove(parentName);
423     }
424 
425     /**
426      * Clears the errorPath.
427      */
428     public void clearErrorPath() {
429         errorPath.clear();
430     }
431 
432     /**
433      * This is what's prepended to the beginning of the key. This is built by iterating over all of the entries in the errorPath
434      * list and concatenating them together witha "."
435      *
436      * @return String Returns the keyPath.
437      * @param propertyName
438      * @param prependFullErrorPath
439      */
440     public String getKeyPath(String propertyName, boolean prependFullErrorPath) {
441         String keyPath = "";
442 
443         if (KNSConstants.GLOBAL_ERRORS.equals(propertyName)) {
444             return KNSConstants.GLOBAL_ERRORS;
445         }
446 
447         if (!errorPath.isEmpty() && prependFullErrorPath) {
448             keyPath = StringUtils.join(errorPath.iterator(), ".");
449             keyPath += (keyPath!=null && keyPath.endsWith("."))?propertyName:"." + propertyName;
450         }
451         else {
452             keyPath = propertyName;
453         }
454 
455         return keyPath;
456     }
457 
458     /**
459      * @return List of the property names that have errors.
460      */
461     public List<String> getPropertiesWithErrors() {
462         List<String> properties = new ArrayList<String>();
463 
464         for (Iterator<String> iter = errorMessages.keySet().iterator(); iter.hasNext();) {
465             properties.add(iter.next());
466         }
467 
468         return properties;
469     }
470 
471     /**
472      * @return List of the property names that have warnings.
473      */
474     public List<String> getPropertiesWithWarnings() {
475         List<String> properties = new ArrayList<String>(warningMessages.keySet());
476         return properties;
477     }
478 
479     /**
480      * @return List of the property names that have info.
481      */
482     public List<String> getPropertiesWithInfo() {
483         List<String> properties = new ArrayList<String>(infoMessages.keySet());
484         return properties;
485     }
486 
487     // methods added to complete the Map interface
488     /**
489      * Clears the messages list.
490      *
491      * @deprecated As of rice 0.9.4, use {@link #clearErrorMessages()} instead
492      */
493     @Deprecated
494     public void clear() {
495         clearErrorMessages();
496     }
497 
498     public void clearErrorMessages() {
499     	errorMessages.clear();
500     }
501 
502     /**
503      * @deprecated As of rice 0.9.4, use {@link #doesPropertyHaveError(String)} instead
504      */
505     @Deprecated
506     public boolean containsKey(Object key) {
507         return doesPropertyHaveError((String) key);
508     }
509 
510     public boolean doesPropertyHaveError(String key) {
511     	return errorMessages.containsKey(key);
512     }
513 
514     /**
515      * @param pattern comma separated list of keys, optionally ending with * wildcard
516      */
517     public boolean containsKeyMatchingPattern(String pattern) {
518         ArrayList simplePatterns = new ArrayList();
519         ArrayList wildcardPatterns = new ArrayList();
520         String[] patterns = pattern.split(",");
521         for (int i = 0; i < patterns.length; i++) {
522             String s = patterns[i];
523             if (s.endsWith("*")) {
524                 wildcardPatterns.add(s.substring(0, s.length() - 1));
525             }
526             else {
527                 simplePatterns.add(s);
528             }
529         }
530         for (Iterator<String> keys = errorMessages.keySet().iterator(); keys.hasNext();) {
531             String key = (String) keys.next();
532             if (simplePatterns.contains(key)) {
533                 return true;
534             }
535             for (Iterator wildcardIterator = wildcardPatterns.iterator(); wildcardIterator.hasNext();) {
536                 String wildcard = (String) wildcardIterator.next();
537                 if (key.startsWith(wildcard)) {
538                     return true;
539                 }
540             }
541         }
542         return false;
543     }
544 
545     /**
546      * @deprecated As of rice 0.9.4, use {@link #getAllPropertiesAndErrors()} instead
547      */
548     @Deprecated
549     public Set entrySet() {
550         return getAllPropertiesAndErrors();
551     }
552 
553     public Set<Map.Entry<String, TypedArrayList>> getAllPropertiesAndErrors() {
554     	return errorMessages.entrySet();
555     }
556 
557     /**
558      * @deprecated As of rice 0.9.4, use {@link #getErrorMessagesForProperty(String)} instead
559      */
560     @Deprecated
561     public Object get(Object key) {
562         return getErrorMessagesForProperty((String) key);
563     }
564 
565     public TypedArrayList getErrorMessagesForProperty(String propertyName) {
566     	return errorMessages.get(propertyName);
567     }
568 
569     public TypedArrayList getWarningMessagesForProperty(String propertyName) {
570     	return warningMessages.get(propertyName);
571     }
572 
573     public TypedArrayList getInfoMessagesForProperty(String propertyName) {
574     	return infoMessages.get(propertyName);
575     }
576 
577     /**
578      * @deprecated As of rice 0.9.4, use {@link #hasNoErrors()} instead
579      */
580     @Deprecated
581     public boolean isEmpty() {
582         return hasNoErrors();
583     }
584 
585     public boolean hasErrors() {
586     	return !errorMessages.isEmpty();
587     }
588 
589     public boolean hasNoErrors() {
590     	return errorMessages.isEmpty();
591     }
592 
593     public boolean hasWarnings() {
594     	return !warningMessages.isEmpty();
595     }
596 
597     public boolean hasNoWarnings() {
598     	return warningMessages.isEmpty();
599     }
600 
601     public boolean hasInfo() {
602     	return !infoMessages.isEmpty();
603     }
604 
605     public boolean hasNoInfo() {
606     	return infoMessages.isEmpty();
607     }
608 
609     public boolean hasMessages() {
610         if (!errorMessages.isEmpty()
611                 || !warningMessages.isEmpty()
612                 || !infoMessages.isEmpty()) {
613             return true;
614         }
615         return false;
616     }
617 
618     public boolean hasNoMessages() {
619         if (errorMessages.isEmpty()
620                 && warningMessages.isEmpty()
621                 && infoMessages.isEmpty()) {
622             return true;
623         }
624         return false;
625     }
626 
627     /**
628      * @deprecated As of rice 0.9.4, use {@link #getAllPropertiesWithErrors()} instead
629      */
630     @Deprecated
631     public Set keySet() {
632         return getAllPropertiesWithErrors();
633     }
634 
635     public Set<String> getAllPropertiesWithErrors() {
636     	return errorMessages.keySet();
637     }
638 
639     public Set<String> getAllPropertiesWithWarnings() {
640     	return warningMessages.keySet();
641     }
642 
643     public Set<String> getAllPropertiesWithInfo() {
644     	return infoMessages.keySet();
645     }
646 
647     /**
648      * @deprecated as of rice 0.9.4, use {@link #removeAllErrorMessagesForProperty(String)} instead
649      */
650     @Deprecated
651     public Object remove(Object key) {
652         return removeAllErrorMessagesForProperty((String) key);
653     }
654 
655     public TypedArrayList removeAllErrorMessagesForProperty(String property) {
656     	return errorMessages.remove(property);
657     }
658 
659     public TypedArrayList removeAllWarningMessagesForProperty(String property) {
660     	return warningMessages.remove(property);
661     }
662 
663     public TypedArrayList removeAllInfoMessagesForProperty(String property) {
664     	return infoMessages.remove(property);
665     }
666 
667     /**
668      * @deprecated As of rice 0.9.4, use {@link #getNumberOfPropertiesWithErrors()} instead
669      */
670     @Deprecated
671     public int size() {
672         return getNumberOfPropertiesWithErrors();
673     }
674 
675     public int getNumberOfPropertiesWithErrors() {
676     	return errorMessages.size();
677     }
678 
679     // forbidden-but-required operations
680     /**
681      * @deprecated As of rice 0.9.4, deprecated because this method always throws an {@link UnsupportedOperationException}
682      */
683     @Deprecated
684     public Object put(Object key, Object value) {
685         throw new UnsupportedOperationException();
686     }
687 
688     /**
689      * @deprecated As of rice 0.9.4, deprecated because this method always throws an {@link UnsupportedOperationException}
690      */
691     @Deprecated
692     public void putAll(Map arg0) {
693         throw new UnsupportedOperationException();
694     }
695 
696     /**
697      * @deprecated As of rice 0.9.4, this method has been deprecated since it always throws a {@link UnsupportedOperationException}
698      */
699     @Deprecated
700     public boolean containsValue(Object value) {
701         throw new UnsupportedOperationException();
702     }
703 
704     /**
705      * @deprecated As of rice 0.9.4, deprecated because this method always throws an {@link UnsupportedOperationException}
706      */
707     @Deprecated
708     public Collection values() {
709         throw new UnsupportedOperationException();
710     }
711 
712     /**
713      * Renders as a String, to help debug tests.
714      *
715      * @return a String, to help debug tests.
716      */
717     @Override
718     public String toString() {
719         return "ErrorMap (errorPath = " + errorPath + ", messages = " + errorMessages + ")";
720     }
721 
722 
723     /**
724      * @see java.lang.Object#equals(java.lang.Object)
725      */
726     @Override
727     public boolean equals(Object obj) {
728         boolean equals = false;
729 
730         if (this == obj) {
731             equals = true;
732         }
733         else if (obj instanceof MessageMap) {
734             MessageMap other = (MessageMap) obj;
735 
736             if (getErrorPath().equals(other.getErrorPath())) {
737                 if (size() == other.size()) {
738                     if (entrySet().equals(other.entrySet())) {
739                         equals = true;
740                     }
741                 }
742             }
743         }
744 
745         return equals;
746     }
747 
748     /**
749      * Returns the size, since that meets with the requirements of the hashCode contract, and since I don't expect ErrorMap to be
750      * used as the key in a Map.
751      *
752      * @see java.lang.Object#hashCode()
753      */
754     @Override
755     public int hashCode() {
756         return size();
757     }
758 
759     public Map<String, TypedArrayList> getErrorMessages() {
760         return this.errorMessages;
761     }
762 
763     public Map<String, TypedArrayList> getWarningMessages() {
764         return this.warningMessages;
765     }
766 
767     public Map<String, TypedArrayList> getInfoMessages() {
768         return this.infoMessages;
769     }
770 }