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