001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.resources.impl;
019
020 import java.io.Serializable;
021 import java.util.ArrayList;
022 import java.util.Collections;
023 import java.util.Comparator;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028
029 import org.apache.commons.resources.Message;
030 import org.apache.commons.resources.MessageList;
031
032 /**
033 * A basic implementation of a MessageList.
034 * <p>
035 * Orginally based on org.apache.struts.action.ActionMessages, Revision 49670.
036 */
037 public class BasicMessageList implements Serializable, MessageList {
038
039 /**
040 * Compares MessageItems.
041 */
042 private static final Comparator ACTION_ITEM_COMPARATOR = new Comparator() {
043 public int compare(Object o1, Object o2) {
044 return ((MessageItem) o1).getOrder() - ((MessageItem) o2).getOrder();
045 }
046 };
047
048 /**
049 * Have the messages been retrieved from this object?
050 */
051 private boolean accessed = false;
052
053 /**
054 * The accumulated set of <code>Message</code> objects (represented
055 * as a List) for each property, keyed by property name.
056 */
057 private Map messages = new HashMap();
058
059 /**
060 * The current number of the property/key being added. This is used
061 * to maintain the order messages are added.
062 */
063 private int count = 0;
064
065 // --------------------------------------------------------- Public Methods
066
067 /**
068 * Create an empty <code>MessageList</code> object.
069 */
070 public BasicMessageList() {
071 super();
072 }
073
074 /**
075 * Create an <code>MessageList</code> object initialized to use
076 * the given value for the "global" message key.
077 *
078 * @param globalMessageKey The new default global message key
079 */
080 public BasicMessageList(String globalMessageKey) {
081 super();
082 this.setGlobalMessageKey(globalMessageKey);
083 }
084
085 /**
086 * Create an <code>MessageList</code> object initialized with the given
087 * messages.
088 *
089 * @param messages The messages to be initially added to this object.
090 */
091 public BasicMessageList(MessageList messages) {
092 super();
093 this.add(messages);
094 }
095
096 /**
097 * Create an <code>MessageList</code> object initialized with the given
098 * messages and the given global message key.
099 *
100 * @param globalMessageKey The new default global message key
101 * @param messages The messages to be initially added to this object.
102 *
103 */
104 public BasicMessageList(String globalMessageKey, MessageList messages) {
105 super();
106 this.setGlobalMessageKey(globalMessageKey);
107 this.add(messages);
108 }
109
110 /**
111 * The "global" message key for this MessageList
112 * [GLOBAL_MESSAGE_KEY].
113 */
114 private String globalMessageKey = GLOBAL_MESSAGE_KEY;
115
116 /**
117 * @return The default global message key
118 */
119 public String getGlobalMessageKey() {
120 return this.globalMessageKey;
121 }
122
123 /**
124 * @param globalMessageKey The new default global message key
125 */
126 public void setGlobalMessageKey(String globalMessageKey) {
127 this.globalMessageKey = globalMessageKey;
128 }
129
130 /**
131 * Add a message to the set of messages for the specified property. An
132 * order of the property/key is maintained based on the initial addition
133 * of the property/key.
134 *
135 * @param property Property name (or MessageList.GLOBAL_MESSAGE_KEY)
136 * @param message The message to be added
137 */
138 public void add(String property, Message message) {
139
140 MessageItem item = (MessageItem) messages.get(property);
141 List list = null;
142
143 if (item == null) {
144 list = new ArrayList();
145 item = new MessageItem(list, this.count++);
146
147 messages.put(property, item);
148 } else {
149 list = item.getList();
150 }
151
152 list.add(message);
153
154 }
155
156 /**
157 * Add a message to the set of messages for the "global" property. An
158 * order of the property/key is maintained based on the initial addition
159 * of the property/key.
160 *
161 * @param message The message to be added
162 */
163 public void add(Message message) {
164 this.add(getGlobalMessageKey(), message);
165 }
166
167 /**
168 * Adds the messages from the given <code>MessageList</code> object to
169 * this set of messages. The messages are added in the order they are returned from
170 * the properties() method. If a message's property is already in the current
171 * <code>MessageList</code> object it is added to the end of the list for that
172 * property. If a message's property is not in the current list it is added to the end
173 * of the properties.
174 *
175 * @param messageList The <code>MessageList</code> object to be added.
176 */
177 public void add(MessageList messageList) {
178 // loop over properties
179 Iterator props = messageList.properties();
180 while (props.hasNext()) {
181 String property = (String) props.next();
182
183 // loop over messages for each property
184 Iterator msgs = messageList.get(property);
185 while (msgs.hasNext()) {
186 Message msg = (Message) msgs.next();
187 this.add(property, msg);
188 }
189
190 }
191 }
192
193 /**
194 * Clear all messages recorded by this object.
195 */
196 public void clear() {
197 this.messages.clear();
198 }
199
200 /**
201 * Determines if the MessageList's messages have been accessed one or more
202 * times. Returns <code>true</code> if the <code>get()</code> or
203 * <code>get(String)</code> methods are called.
204 * @return <code>true</code> if the messages have been accessed one or more
205 * times.
206 */
207 public boolean isAccessed() {
208 return this.accessed;
209 }
210
211 /**
212 * @return Return <code>true</code> if there are no messages recorded
213 * in this collection, or <code>false</code> otherwise.
214 */
215 public boolean isEmpty() {
216 return messages.isEmpty();
217 }
218
219 /**
220 * Return the set of all recorded messages, without distinction
221 * by which property the messages are associated with. If there are
222 * no messages recorded, an empty enumeration is returned.
223 *
224 * @return All messages.
225 */
226 public Iterator get() {
227 this.accessed = true;
228
229 if (messages.isEmpty()) {
230 return Collections.EMPTY_LIST.iterator();
231 }
232
233 ArrayList results = new ArrayList();
234 ArrayList actionItems = new ArrayList();
235
236 for (Iterator i = messages.values().iterator(); i.hasNext();) {
237 actionItems.add(i.next());
238 }
239
240 // Sort MessageItems based on the initial order the
241 // property/key was added to MessageList.
242 Collections.sort(actionItems, ACTION_ITEM_COMPARATOR);
243
244 for (Iterator i = actionItems.iterator(); i.hasNext();) {
245 MessageItem ami = (MessageItem) i.next();
246
247 for (Iterator msgs = ami.getList().iterator(); msgs.hasNext();) {
248 results.add(msgs.next());
249 }
250 }
251
252 return results.iterator();
253 }
254
255 /**
256 * Return the set of messages related to a specific property.
257 * If there are no such messages, an empty enumeration is returned.
258 *
259 * @param property Property name
260 * @return Messages related to a specific property.
261 */
262 public Iterator get(String property) {
263 this.accessed = true;
264
265 MessageItem item = (MessageItem) messages.get(property);
266
267 return (item == null)
268 ? Collections.EMPTY_LIST.iterator()
269 : item.getList().iterator();
270 }
271
272 /**
273 * Return the set of property names for which at least one message has
274 * been recorded. If there are no messages, an empty Iterator is returned.
275 * If you have recorded global messages, the String value of
276 * <code>MessageList.GLOBAL_MESSAGE</code> will be one of the returned
277 * property names.
278 *
279 * @return The property names.
280 */
281 public Iterator properties() {
282 return messages.keySet().iterator();
283 }
284
285 /**
286 * Return the number of messages recorded for all properties (including
287 * global messages). <strong>NOTE</strong> - it is more efficient to call
288 * <code>isEmpty()</code> if all you care about is whether or not there are
289 * any messages at all.
290 *
291 * @return The number of messages.
292 */
293 public int size() {
294
295 int total = 0;
296
297 for (Iterator i = messages.values().iterator(); i.hasNext();) {
298 MessageItem ami = (MessageItem) i.next();
299 total += ami.getList().size();
300 }
301
302 return total;
303 }
304
305 /**
306 * Return the number of messages associated with the specified property.
307 *
308 * @param property Property name (or MessageList.GLOBAL_MESSAGE_KEY
309 * @return The number of messages for a specific property.
310 */
311 public int size(String property) {
312
313 MessageItem ami = (MessageItem) messages.get(property);
314
315 return (ami == null) ? 0 : ami.getList().size();
316 }
317
318 /**
319 * Returns a String representation of this MessageList's
320 * [property name]=[message list] mapping.
321 * @see java.lang.Object#toString()
322 */
323 public String toString() {
324 return this.messages.toString();
325 }
326
327 /**
328 * Holds messages for a specified property.
329 */
330 protected static class MessageItem implements Serializable {
331
332 /**
333 * The list of <code>Message</code>s.
334 */
335 private List list = null;
336
337 /**
338 * The position in the list of messages.
339 */
340 private int order = 0;
341
342 /**
343 * Construct a MessageItem with a list of messages
344 * and specified order.
345 * @param list List of Messages.
346 * @param order Order.
347 */
348 public MessageItem(List list, int order) {
349 this.list = list;
350 this.order = order;
351 }
352
353 /**
354 * @return The list of messsages.
355 */
356 public List getList() {
357 return list;
358 }
359
360 /**
361 * @param list The list of messsages.
362 */
363 public void setList(List list) {
364 this.list = list;
365 }
366
367 /**
368 * @return The order.
369 */
370 public int getOrder() {
371 return order;
372 }
373
374 /**
375 * @param order The order.
376 */
377 public void setOrder(int order) {
378 this.order = order;
379 }
380
381 }
382
383 }