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    }