View Javadoc

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