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 */
017package org.apache.wicket.feedback;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.concurrent.CopyOnWriteArrayList;
025
026import org.apache.wicket.Component;
027import org.apache.wicket.util.io.IClusterable;
028import org.apache.wicket.util.string.StringList;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032/**
033 * Holds list of feedback messages. The list can be added to, cleared, queried and filtered.
034 * <p>
035 * WARNING: This class should typically NOT be used directly.
036 * <p>
037 * 
038 * @author Eelco Hillenius
039 * @author Jonathan Locke
040 */
041public final class FeedbackMessages implements IClusterable, Iterable<FeedbackMessage>
042{
043        /** Log. */
044        private static final Logger log = LoggerFactory.getLogger(FeedbackMessages.class);
045
046        private static final long serialVersionUID = 1L;
047
048        /**
049         * Holds a list of {@link org.apache.wicket.feedback.FeedbackMessage}s.
050         */
051        private final List<FeedbackMessage> messages;
052
053        /**
054         * Construct.
055         */
056        public FeedbackMessages()
057        {
058                messages = new CopyOnWriteArrayList<>();
059        }
060
061        /**
062         * Adds a message.
063         * 
064         * @param message
065         *            the message
066         */
067        public final void add(FeedbackMessage message)
068        {
069                log.debug("Adding feedback message '{}'", message);
070
071                synchronized (messages)
072                {
073                        messages.add(message);
074                }
075        }
076
077        /**
078         * Adds a message
079         * 
080         * @param reporter
081         * @param message
082         * @param level
083         */
084        public final void add(Component reporter, Serializable message, int level)
085        {
086                add(new FeedbackMessage(reporter, message, level));
087        }
088
089        /**
090         * Adds a new ui message with level DEBUG to the current messages.
091         * 
092         * @param reporter
093         *            the reporting component
094         * @param message
095         *            the actual message
096         */
097        public final void debug(Component reporter, Serializable message)
098        {
099                add(new FeedbackMessage(reporter, message, FeedbackMessage.DEBUG));
100        }
101
102        /**
103         * Adds a new ui message with level INFO to the current messages.
104         * 
105         * @param reporter
106         *            The reporting component
107         * @param message
108         *            The actual message
109         */
110        public final void info(Component reporter, Serializable message)
111        {
112                add(new FeedbackMessage(reporter, message, FeedbackMessage.INFO));
113        }
114
115        /**
116         * Adds a new ui message with level SUCCESS to the current messages.
117         * 
118         * @param reporter
119         *            The reporting component
120         * @param message
121         *            The actual message
122         */
123        public final void success(Component reporter, Serializable message)
124        {
125                add(new FeedbackMessage(reporter, message, FeedbackMessage.SUCCESS));
126        }
127
128        /**
129         * Adds a new ui message with level WARNING to the current messages.
130         * 
131         * @param reporter
132         *            the reporting component
133         * @param message
134         *            the actual message
135         */
136        public final void warn(Component reporter, Serializable message)
137        {
138                add(new FeedbackMessage(reporter, message, FeedbackMessage.WARNING));
139        }
140
141        /**
142         * Adds a new ui message with level ERROR to the current messages.
143         * 
144         * @param reporter
145         *            the reporting component
146         * @param message
147         *            the actual message
148         */
149        public final void error(Component reporter, Serializable message)
150        {
151                add(new FeedbackMessage(reporter, message, FeedbackMessage.ERROR));
152        }
153
154        /**
155         * Adds a new ui message with level FATAL to the current messages.
156         * 
157         * @param reporter
158         *            the reporting component
159         * @param message
160         *            the actual message
161         */
162        public final void fatal(Component reporter, Serializable message)
163        {
164                add(new FeedbackMessage(reporter, message, FeedbackMessage.FATAL));
165        }
166
167        /**
168         * Clears any existing messages.
169         * 
170         * @return The number of messages deleted
171         */
172        public final int clear()
173        {
174                return clear(null);
175        }
176
177        /**
178         * Clears all messages that are accepted by the filter.
179         * 
180         * @param filter
181         *            Filter for selecting messages. If null, all messages will be returned
182         * @return The number of messages deleted
183         */
184        public final int clear(final IFeedbackMessageFilter filter)
185        {
186                if (messages.isEmpty())
187                {
188                        return 0;
189                }
190
191                List<FeedbackMessage> toDelete = messages(filter);
192
193                for (FeedbackMessage message : toDelete)
194                {
195                        message.detach();
196                }
197
198                synchronized(messages)
199                {
200                        int sizeBefore = messages.size();
201                        messages.removeAll(toDelete);
202                        int sizeAfter = messages.size();
203                        return sizeAfter - sizeBefore;
204                }
205        }
206
207        /**
208         * @param filter
209         *            Filter for selecting messages
210         * @return True if one or more messages matches the filter
211         */
212        public final boolean hasMessage(final IFeedbackMessageFilter filter)
213        {
214                for (final FeedbackMessage message : messages)
215                {
216                        if (filter == null || filter.accept(message))
217                        {
218                                return true;
219                        }
220                }
221                return false;
222        }
223
224        /**
225         * Checks if a message of the specified {@code level} or greater was registered.<br />
226         * To check for a precise {@code level} use {@link #hasMessage(IFeedbackMessageFilter)}
227         * and pass it a reference to {@link org.apache.wicket.feedback.ExactLevelFeedbackMessageFilter}.
228         * 
229         * @param level
230         *            The level of the message
231         * @return {@code true} if a message with the specified {@code level} or greater was registered
232         */
233        public final boolean hasMessage(final int level)
234        {
235                for (FeedbackMessage message : messages)
236                {
237                        if (message.isLevel(level))
238                        {
239                                return true;
240                        }
241                }
242                return false;
243        }
244
245        /**
246         * Retrieves the first message
247         * 
248         * @return message or {@code null} if none
249         */
250        public final FeedbackMessage first()
251        {
252                return messages.size() > 0 ? messages.get(0) : null;
253        }
254
255        /**
256         * Retrieves the first message that level is greater than or equal to the given level
257         * 
258         * @param level
259         *            The minimum level of the message
260         * @return a message with the same or a higher level, or {@code null} if none
261         */
262        public final FeedbackMessage first(final int level)
263        {
264                for (FeedbackMessage message : messages)
265                {
266                        if (message.isLevel(level))
267                        {
268                                return message;
269                        }
270                }
271                return null;
272        }
273
274        /**
275         * Gets an iterator over stored messages
276         * 
277         * @return iterator over stored messages
278         */
279        @Override
280        public final Iterator<FeedbackMessage> iterator()
281        {
282                return messages.iterator();
283        }
284
285        /**
286         * Gets a list of messages from the page using a filter.
287         * 
288         * @param filter
289         *            Filter for selecting messages. If null, all messages will be returned
290         * @return The messages or an empty list if no messages are found
291         */
292        public final List<FeedbackMessage> messages(final IFeedbackMessageFilter filter)
293        {
294                if (messages.isEmpty())
295                {
296                        return Collections.emptyList();
297                }
298
299                final List<FeedbackMessage> list = new ArrayList<FeedbackMessage>();
300                for (final FeedbackMessage message : messages)
301                {
302                        if (filter == null || filter.accept(message))
303                        {
304                                list.add(message);
305                        }
306                }
307                return list;
308        }
309
310        /**
311         * Gets whether there are no messages.
312         * 
313         * @return True when there are no messages
314         */
315        public final boolean isEmpty()
316        {
317                return messages.isEmpty();
318        }
319
320        /**
321         * Gets the number of messages
322         * 
323         * @return the number of messages
324         */
325        public final int size()
326        {
327                return messages.size();
328        }
329
330        /**
331         * Gets the number of messages.
332         * 
333         * @param filter
334         *            Filter for counting messages. If null, the count of all messages will be returned
335         * 
336         * @return the number of messages
337         */
338        public final int size(final IFeedbackMessageFilter filter)
339        {
340                int count = 0;
341                for (final FeedbackMessage message : messages)
342                {
343                        if (filter == null || filter.accept(message))
344                        {
345                                count++;
346                        }
347                }
348                return count;
349        }
350
351        /**
352         * @see java.lang.Object#toString()
353         */
354        @Override
355        public String toString()
356        {
357                return "[feedbackMessages = " + StringList.valueOf(messages) + ']';
358        }
359
360        /**
361         * Retrieves all stored messages as an unmodifiable list
362         * 
363         * @return stored messages as unmodifiable list
364         */
365        public List<FeedbackMessage> toList()
366        {
367                return Collections.unmodifiableList(messages);
368        }
369
370        /**
371         * Detaches each stored message
372         */
373        public void detach()
374        {
375                for (FeedbackMessage message : messages)
376                {
377                        message.detach();
378                }
379        }
380}