View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.platform.swing.builder.utils;
17  
18  import javax.swing.JDialog;
19  import javax.swing.JOptionPane;
20  import java.awt.Component;
21  
22  import net.sf.jguiraffe.gui.builder.utils.MessageOutput;
23  import net.sf.jguiraffe.gui.builder.window.Window;
24  import net.sf.jguiraffe.gui.builder.window.WindowUtils;
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.commons.lang.WordUtils;
27  
28  /**
29   * <p>
30   * A Swing specific implementation of the {@code MessageOutput}
31   * interface.
32   * </p>
33   * <p>
34   * This implementation makes use of {@code JOptionPane} for displaying
35   * message boxes.
36   * </p>
37   *
38   * @author Oliver Heger
39   * @version $Id: SwingMessageOutput.java 205 2012-01-29 18:29:57Z oheger $
40   */
41  public class SwingMessageOutput implements MessageOutput
42  {
43      /**
44       * Constant for a line length which disables line wrapping. If this value is
45       * passed to the constructor, the message text is not wrapped into multiple
46       * lines.
47       *
48       * @since 1.3
49       */
50      public static final int NO_LINE_WRAP = -1;
51  
52      /** An array with the supported message types. */
53      private static final int[] MESSAGE_TYPES = {
54              MESSAGE_ERROR, MESSAGE_INFO, MESSAGE_PLAIN, MESSAGE_QUESTION,
55              MESSAGE_WARNING
56      };
57  
58      /** An array with the JOptionPane message types. */
59      private static final int[] SWING_MESSAGE_TYPES = {
60              JOptionPane.ERROR_MESSAGE, JOptionPane.INFORMATION_MESSAGE,
61              JOptionPane.PLAIN_MESSAGE, JOptionPane.QUESTION_MESSAGE,
62              JOptionPane.WARNING_MESSAGE
63      };
64  
65      /** An array with the supported button types. */
66      private static final int[] BUTTON_TYPES = {
67              BTN_OK, BTN_OK_CANCEL, BTN_YES_NO, BTN_YES_NO_CANCEL
68      };
69  
70      /** An array with the JOptionPane button types. */
71      private static final int[] SWING_BUTTON_TYPES = {
72              JOptionPane.DEFAULT_OPTION, JOptionPane.OK_CANCEL_OPTION,
73              JOptionPane.YES_NO_OPTION, JOptionPane.YES_NO_CANCEL_OPTION
74      };
75  
76      /** An array with the supported return values. */
77      private static final int[] RETURN_VALUES = {
78              RET_CANCEL, RET_OK, RET_YES, RET_NO, RET_CANCEL
79      };
80  
81      /** An array with the JOptionPane return values. */
82      private static final int[] SWING_RETURN_VALUES = {
83              JOptionPane.CANCEL_OPTION, JOptionPane.OK_OPTION,
84              JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
85              JOptionPane.CLOSED_OPTION
86      };
87  
88      /** Constant for a starting HTML tag. */
89      private static final String HTML_START = "<html>";
90  
91      /** Constant for a closing HTML tag. */
92      private static final String HTML_END = "</html>";
93  
94      /** Constant for the default maximum line length. */
95      private static final int DEFAULT_MAX_LINE_LENGTH = 80;
96  
97      /** Constant for the initial length of the buffer for text processing. */
98      private static final int BUF_LENGTH = 256;
99  
100     /** Constant for the newline character. */
101     private static final String CR = "\n";
102 
103     /** The maximum length of a line for the message text. */
104     private final int maximumLineLength;
105 
106     /**
107      * Creates a new instance of {@code SwingMessageOutput} and sets a default
108      * maximum line length.
109      */
110     public SwingMessageOutput()
111     {
112         this(DEFAULT_MAX_LINE_LENGTH);
113     }
114 
115     /**
116      * Creates a new instance of {@code SwingMessageOutput} with the specified
117      * maximum message line length. Before the message is displayed, it is
118      * ensured that single lines do not exceed this maximum length; if
119      * necessary, the text is split into multiple lines. To disable line
120      * wrapping, the value {@link #NO_LINE_WRAP} can be passed.
121      *
122      * @param maxLineLength the maximum length of a line for the message text
123      *        (in characters); must be &gt; 0
124      * @throws IllegalArgumentException if an invalid line length is passed in
125      * @since 1.3
126      */
127     public SwingMessageOutput(int maxLineLength)
128     {
129         if (maxLineLength != NO_LINE_WRAP && maxLineLength < 1)
130         {
131             throw new IllegalArgumentException(
132                     "Maximum line length must be > 0!");
133         }
134         maximumLineLength = maxLineLength;
135     }
136 
137     /**
138      * Returns the maximum line length for the messages to be displayed.
139      *
140      * @return the maximum line length
141      * @since 1.3
142      */
143     public int getMaximumLineLength()
144     {
145         return maximumLineLength;
146     }
147 
148     /**
149      * Displays a message box.
150      *
151      * @param parent the parent window; this should be <b>null</b> or point to
152      * a Swing window
153      * @param message the message
154      * @param title the message box's title
155      * @param messageType the type of the message
156      * @param buttonType specifies the buttons to be displayed
157      * @return the pressed button
158      */
159     public int show(Window parent, Object message, String title,
160             int messageType, int buttonType)
161     {
162         JOptionPane pane = createOptionPane(parent, message, title,
163                 convertMessageType(messageType), convertButtonType(buttonType));
164         JDialog dlg = createDialog(pane, parent, title);
165         Object result = showPane(pane, dlg);
166         if (result == null || !(result instanceof Integer))
167         {
168             return RET_CANCEL;
169         }
170         else
171         {
172             return convertReturnValue(((Integer) result).intValue());
173         }
174     }
175 
176     /**
177      * Converts the passed in message type into the corresponding type used by
178      * {@code JOptionPane}.
179      *
180      * @param type the type to be converted
181      * @return the corresponding Swing constant
182      */
183     protected int convertMessageType(int type)
184     {
185         return convert(type, MESSAGE_TYPES, SWING_MESSAGE_TYPES);
186     }
187 
188     /**
189      * Converts the passed in button type into the corresponding option type
190      * used by {@code JOptionPane}.
191      *
192      * @param type the type to be converted
193      * @return the corresponding Swing constant
194      */
195     protected int convertButtonType(int type)
196     {
197         return convert(type, BUTTON_TYPES, SWING_BUTTON_TYPES);
198     }
199 
200     /**
201      * Converts the passed in return value from the {@code JOptionPane}
202      * to the corresponding {@code RET_XXXX} constant.
203      *
204      * @param value the return value from the option pane
205      * @return the corresponding {@code RET_XXXX} constant
206      */
207     protected int convertReturnValue(int value)
208     {
209         return convert(value, SWING_RETURN_VALUES, RETURN_VALUES);
210     }
211 
212     /**
213      * Creates the option pane dialog for displaying the message box.
214      *
215      * @param parent the parent window
216      * @param message the message
217      * @param title the title
218      * @param messageType the message type
219      * @param optionType the option type
220      * @return the option pane
221      */
222     protected JOptionPane createOptionPane(Window parent, Object message,
223             String title, int messageType, int optionType)
224     {
225         return new JOptionPane(processMessage(message), messageType, optionType);
226     }
227 
228     /**
229      * Displays the given option pane. This method is called after the pane has
230      * been created and initialized.
231      *
232      * @param pane the pane to display
233      * @param dialog the dialog obtained from the option pane
234      * @return the return value of the pane (indicating the option selected by
235      * the user)
236      */
237     protected Object showPane(JOptionPane pane, JDialog dialog)
238     {
239         dialog.setVisible(true);
240         return pane.getValue();
241     }
242 
243     /**
244      * Creates the dialog from the option pane. This is the component that is to
245      * be displayed.
246      *
247      * @param pane the option pane
248      * @param parent the parent component
249      * @param title the dialog's title
250      * @return the dialog to display
251      */
252     protected JDialog createDialog(JOptionPane pane, Window parent, String title)
253     {
254         return pane.createDialog((parent != null) ? (Component) WindowUtils
255                 .getPlatformWindow(parent) : null, title);
256     }
257 
258     /**
259      * Processes the given message before it is displayed. This method
260      * implements some conversions to ensure that a valid message is displayed
261      * in a visually pleasant way. It does the following changes:
262      * <ul>
263      * <li>If a maximum line length is specified, line wrapping is performed.</li>
264      * <li>If the message string is wrapped in HTML tags, they are removed.</li>
265      * </ul>
266      *
267      * @param message the message
268      * @return the processed message
269      */
270     private Object processMessage(Object message)
271     {
272         if (message == null)
273         {
274             return StringUtils.EMPTY;
275         }
276 
277         String msg = removeHtmlTags(message);
278         if (getMaximumLineLength() != NO_LINE_WRAP)
279         {
280             return lineWrap(msg);
281         }
282         return msg;
283     }
284 
285     /**
286      * Performs line wrapping for the specified message object. For each line in
287      * the message string the maximum line length is enforced.
288      *
289      * @param message the message
290      * @return the message with line wrapping performed
291      */
292     private String lineWrap(String message)
293     {
294         String[] lines = message.split(String.valueOf(CR));
295         StringBuilder buf = new StringBuilder(BUF_LENGTH);
296         boolean first = true;
297 
298         for (String line : lines)
299         {
300             if (first)
301             {
302                 first = false;
303             }
304             else
305             {
306                 buf.append(CR);
307             }
308             buf.append(WordUtils.wrap(line, getMaximumLineLength(), CR, true));
309         }
310         return buf.toString();
311     }
312 
313     /**
314      * Removes leading and trailing html tags from the message string. HTML
315      * formatting is not supported.
316      *
317      * @param message the message
318      * @return the processed message
319      */
320     private static String removeHtmlTags(Object message)
321     {
322         String s = message.toString();
323         return StringUtils.removeEndIgnoreCase(
324                 StringUtils.removeStartIgnoreCase(s, HTML_START), HTML_END);
325     }
326 
327     /**
328      * Converts a value from a source range into a destination range. If the
329      * conversion fails, an exception is thrown.
330      *
331      * @param value the value
332      * @param src the source values
333      * @param dest the destination values
334      * @return the converted value
335      */
336     private static int convert(int value, int[] src, int[] dest)
337     {
338         for (int i = 0; i < src.length; i++)
339         {
340             if (value == src[i])
341             {
342                 return dest[i];
343             }
344         }
345 
346         throw new IllegalArgumentException("Unknown parameter " + value);
347     }
348 }