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