View Javadoc

1   /*
2    * $Id: ActionMessages.java 421119 2006-07-12 04:49:11Z wsmoak $
3    *
4    * Copyright 2001-2004 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts.action;
19  
20  import java.io.Serializable;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  /***
30   * <p>A class that encapsulates messages. Messages can be either global or
31   * they are specific to a particular bean property.</p>
32   *
33   * <p>Each individual message is described by an <code>ActionMessage</code>
34   * object, which contains a message key (to be looked up in an appropriate
35   * message resources database), and up to four placeholder arguments used for
36   * parametric substitution in the resulting message.</p>
37   *
38   * <p><strong>IMPLEMENTATION NOTE</strong> - It is assumed that these objects
39   * are created and manipulated only within the context of a single thread.
40   * Therefore, no synchronization is required for access to internal
41   * collections.</p>
42   *
43   * @version $Rev: 421119 $ $Date: 2005-08-26 21:58:39 -0400 (Fri, 26 Aug 2005)
44   *          $
45   * @since Struts 1.1
46   */
47  public class ActionMessages implements Serializable {
48      /***
49       * <p>Compares ActionMessageItem objects.</p>
50       */
51      private static final Comparator ACTION_ITEM_COMPARATOR =
52          new Comparator() {
53              public int compare(Object o1, Object o2) {
54                  return ((ActionMessageItem) o1).getOrder()
55                  - ((ActionMessageItem) o2).getOrder();
56              }
57          };
58  
59      // ----------------------------------------------------- Manifest Constants
60  
61      /***
62       * <p>The "property name" marker to use for global messages, as opposed to
63       * those related to a specific property.</p>
64       */
65      public static final String GLOBAL_MESSAGE =
66          "org.apache.struts.action.GLOBAL_MESSAGE";
67  
68      // ----------------------------------------------------- Instance Variables
69  
70      /***
71       * <p>Have the messages been retrieved from this object?</p>
72       *
73       * <p>The controller uses this property to determine if session-scoped
74       * messages can be removed.</p>
75       *
76       * @since Struts 1.2
77       */
78      protected boolean accessed = false;
79  
80      /***
81       * <p>The accumulated set of <code>ActionMessage</code> objects
82       * (represented as an ArrayList) for each property, keyed by property
83       * name.</p>
84       */
85      protected HashMap messages = new HashMap();
86  
87      /***
88       * <p>The current number of the property/key being added. This is used to
89       * maintain the order messages are added.</p>
90       */
91      protected int iCount = 0;
92  
93      // --------------------------------------------------------- Public Methods
94  
95      /***
96       * <p>Create an empty <code>ActionMessages</code> object.</p>
97       */
98      public ActionMessages() {
99          super();
100     }
101 
102     /***
103      * <p>Create an <code>ActionMessages</code> object initialized with the
104      * given messages.</p>
105      *
106      * @param messages The messages to be initially added to this object. This
107      *                 parameter can be <code>null</code>.
108      * @since Struts 1.1
109      */
110     public ActionMessages(ActionMessages messages) {
111         super();
112         this.add(messages);
113     }
114 
115     /***
116      * <p>Add a message to the set of messages for the specified property. An
117      * order of the property/key is maintained based on the initial addition
118      * of the property/key.</p>
119      *
120      * @param property Property name (or ActionMessages.GLOBAL_MESSAGE)
121      * @param message  The message to be added
122      */
123     public void add(String property, ActionMessage message) {
124         ActionMessageItem item = (ActionMessageItem) messages.get(property);
125         List list;
126 
127         if (item == null) {
128             list = new ArrayList();
129             item = new ActionMessageItem(list, iCount++, property);
130 
131             messages.put(property, item);
132         } else {
133             list = item.getList();
134         }
135 
136         list.add(message);
137     }
138 
139     /***
140      * <p>Adds the messages from the given <code>ActionMessages</code> object
141      * to this set of messages. The messages are added in the order they are
142      * returned from the <code>properties</code> method. If a message's
143      * property is already in the current <code>ActionMessages</code> object,
144      * it is added to the end of the list for that property. If a message's
145      * property is not in the current list it is added to the end of the
146      * properties.</p>
147      *
148      * @param actionMessages The <code>ActionMessages</code> object to be
149      *                       added. This parameter can be <code>null</code>.
150      * @since Struts 1.1
151      */
152     public void add(ActionMessages actionMessages) {
153         if (actionMessages == null) {
154             return;
155         }
156 
157         // loop over properties
158         Iterator props = actionMessages.properties();
159 
160         while (props.hasNext()) {
161             String property = (String) props.next();
162 
163             // loop over messages for each property
164             Iterator msgs = actionMessages.get(property);
165 
166             while (msgs.hasNext()) {
167                 ActionMessage msg = (ActionMessage) msgs.next();
168 
169                 this.add(property, msg);
170             }
171         }
172     }
173 
174     /***
175      * <p>Clear all messages recorded by this object.</p>
176      */
177     public void clear() {
178         messages.clear();
179     }
180 
181     /***
182      * <p>Return <code>true</code> if there are no messages recorded in this
183      * collection, or <code>false</code> otherwise.</p>
184      *
185      * @return <code>true</code> if there are no messages recorded in this
186      *         collection; <code>false</code> otherwise.
187      * @since Struts 1.1
188      */
189     public boolean isEmpty() {
190         return (messages.isEmpty());
191     }
192 
193     /***
194      * <p>Return the set of all recorded messages, without distinction by
195      * which property the messages are associated with. If there are no
196      * messages recorded, an empty enumeration is returned.</p>
197      *
198      * @return An iterator over the messages for all properties.
199      */
200     public Iterator get() {
201         this.accessed = true;
202 
203         if (messages.isEmpty()) {
204             return Collections.EMPTY_LIST.iterator();
205         }
206 
207         ArrayList results = new ArrayList();
208         ArrayList actionItems = new ArrayList();
209 
210         for (Iterator i = messages.values().iterator(); i.hasNext();) {
211             actionItems.add(i.next());
212         }
213 
214         // Sort ActionMessageItems based on the initial order the
215         // property/key was added to ActionMessages.
216         Collections.sort(actionItems, ACTION_ITEM_COMPARATOR);
217 
218         for (Iterator i = actionItems.iterator(); i.hasNext();) {
219             ActionMessageItem ami = (ActionMessageItem) i.next();
220 
221             for (Iterator msgsIter = ami.getList().iterator();
222                 msgsIter.hasNext();) {
223                 results.add(msgsIter.next());
224             }
225         }
226 
227         return results.iterator();
228     }
229 
230     /***
231      * <p>Return the set of messages related to a specific property. If there
232      * are no such messages, an empty enumeration is returned.</p>
233      *
234      * @param property Property name (or ActionMessages.GLOBAL_MESSAGE)
235      * @return An iterator over the messages for the specified property.
236      */
237     public Iterator get(String property) {
238         this.accessed = true;
239 
240         ActionMessageItem item = (ActionMessageItem) messages.get(property);
241 
242         if (item == null) {
243             return (Collections.EMPTY_LIST.iterator());
244         } else {
245             return (item.getList().iterator());
246         }
247     }
248 
249     /***
250      * <p>Returns <code>true</code> if the <code>get()</code> or
251      * <code>get(String)</code> methods are called.</p>
252      *
253      * @return <code>true</code> if the messages have been accessed one or
254      *         more times.
255      * @since Struts 1.2
256      */
257     public boolean isAccessed() {
258         return this.accessed;
259     }
260 
261     /***
262      * <p>Return the set of property names for which at least one message has
263      * been recorded. If there are no messages, an empty <code>Iterator</code>
264      * is returned. If you have recorded global messages, the
265      * <code>String</code> value of <code>ActionMessages.GLOBAL_MESSAGE</code>
266      * will be one of the returned property names.</p>
267      *
268      * @return An iterator over the property names for which messages exist.
269      */
270     public Iterator properties() {
271         if (messages.isEmpty()) {
272             return Collections.EMPTY_LIST.iterator();
273         }
274 
275         ArrayList results = new ArrayList();
276         ArrayList actionItems = new ArrayList();
277 
278         for (Iterator i = messages.values().iterator(); i.hasNext();) {
279             actionItems.add(i.next());
280         }
281 
282         // Sort ActionMessageItems based on the initial order the
283         // property/key was added to ActionMessages.
284         Collections.sort(actionItems, ACTION_ITEM_COMPARATOR);
285 
286         for (Iterator i = actionItems.iterator(); i.hasNext();) {
287             ActionMessageItem ami = (ActionMessageItem) i.next();
288 
289             results.add(ami.getProperty());
290         }
291 
292         return results.iterator();
293     }
294 
295     /***
296      * <p>Return the number of messages recorded for all properties (including
297      * global messages). <strong>NOTE</strong> - it is more efficient to call
298      * <code>isEmpty</code> if all you care about is whether or not there are
299      * any messages at all.</p>
300      *
301      * @return The number of messages associated with all properties.
302      */
303     public int size() {
304         int total = 0;
305 
306         for (Iterator i = messages.values().iterator(); i.hasNext();) {
307             ActionMessageItem ami = (ActionMessageItem) i.next();
308 
309             total += ami.getList().size();
310         }
311 
312         return (total);
313     }
314 
315     /***
316      * <p>Return the number of messages associated with the specified
317      * property. </p>
318      *
319      * @param property Property name (or ActionMessages.GLOBAL_MESSAGE)
320      * @return The number of messages associated with the property.
321      */
322     public int size(String property) {
323         ActionMessageItem item = (ActionMessageItem) messages.get(property);
324 
325         return (item == null) ? 0 : item.getList().size();
326     }
327 
328     /***
329      * <p>Returns a String representation of this ActionMessages' property
330      * name=message list mapping.</p>
331      *
332      * @return String representation of the messages
333      * @see Object#toString()
334      */
335     public String toString() {
336         return this.messages.toString();
337     }
338 
339     /***
340      * <p>This class is used to store a set of messages associated with a
341      * property/key and the position it was initially added to list.</p>
342      */
343     protected class ActionMessageItem implements Serializable {
344         /***
345          * <p>The list of <code>ActionMessage</code>s.</p>
346          */
347         protected List list = null;
348 
349         /***
350          * <p>The position in the list of messages.</p>
351          */
352         protected int iOrder = 0;
353 
354         /***
355          * <p>The property associated with <code>ActionMessage</code>.</p>
356          */
357         protected String property = null;
358 
359         /***
360          * <p>Construct an instance of this class.</p>
361          *
362          * @param list     The list of ActionMessages.
363          * @param iOrder   The position in the list of messages.
364          * @param property The property associated with ActionMessage.
365          */
366         public ActionMessageItem(List list, int iOrder, String property) {
367             this.list = list;
368             this.iOrder = iOrder;
369             this.property = property;
370         }
371 
372         /***
373          * <p>Retrieve the list of messages associated with this item.</p>
374          *
375          * @return The list of messages associated with this item.
376          */
377         public List getList() {
378             return list;
379         }
380 
381         /***
382          * <p>Set the list of messages associated with this item.</p>
383          *
384          * @param list The list of messages associated with this item.
385          */
386         public void setList(List list) {
387             this.list = list;
388         }
389 
390         /***
391          * <p>Retrieve the position in the message list.</p>
392          *
393          * @return The position in the message list.
394          */
395         public int getOrder() {
396             return iOrder;
397         }
398 
399         /***
400          * <p>Set the position in the message list.</p>
401          *
402          * @param iOrder The position in the message list.
403          */
404         public void setOrder(int iOrder) {
405             this.iOrder = iOrder;
406         }
407 
408         /***
409          * <p>Retrieve the property associated with this item.</p>
410          *
411          * @return The property associated with this item.
412          */
413         public String getProperty() {
414             return property;
415         }
416 
417         /***
418          * <p>Set the property associated with this item.</p>
419          *
420          * @param property The property associated with this item.
421          */
422         public void setProperty(String property) {
423             this.property = property;
424         }
425 
426         /***
427          * <p>Construct a string representation of this object.</p>
428          *
429          * @return A string representation of this object.
430          */
431         public String toString() {
432             return this.list.toString();
433         }
434     }
435 }