1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
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
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
158 Iterator props = actionMessages.properties();
159
160 while (props.hasNext()) {
161 String property = (String) props.next();
162
163
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
215
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
283
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 }