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