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    package org.apache.commons.math;
018    
019    import java.io.EOFException;
020    import java.io.IOException;
021    import java.io.PrintStream;
022    import java.io.PrintWriter;
023    import java.text.MessageFormat;
024    import java.text.ParseException;
025    import java.util.ConcurrentModificationException;
026    import java.util.Locale;
027    import java.util.MissingResourceException;
028    import java.util.NoSuchElementException;
029    import java.util.ResourceBundle;
030    
031    /**
032    * Base class for commons-math unchecked exceptions.
033    *
034    * @version $Revision: 822850 $ $Date: 2009-10-07 14:56:42 -0400 (Wed, 07 Oct 2009) $
035    * @since 2.0
036    */
037    public class MathRuntimeException extends RuntimeException {
038    
039        /** Serializable version identifier. */
040        private static final long serialVersionUID = -5128983364075381060L;
041    
042        /**
043         * Pattern used to build the message.
044         */
045        private final String pattern;
046    
047        /**
048         * Arguments used to build the message.
049         */
050        private final Object[] arguments;
051    
052        /**
053         * Constructs a new <code>MathRuntimeException</code> with specified
054         * formatted detail message.
055         * Message formatting is delegated to {@link java.text.MessageFormat}.
056         * @param pattern format specifier
057         * @param arguments format arguments
058         */
059        public MathRuntimeException(final String pattern, final Object ... arguments) {
060            this.pattern   = pattern;
061            this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
062        }
063    
064        /**
065         * Constructs a new <code>MathRuntimeException</code> with specified
066         * nested <code>Throwable</code> root cause.
067         *
068         * @param rootCause  the exception or error that caused this exception
069         *                   to be thrown.
070         */
071        public MathRuntimeException(final Throwable rootCause) {
072            super(rootCause);
073            this.pattern   = getMessage();
074            this.arguments = new Object[0];
075        }
076    
077        /**
078         * Constructs a new <code>MathRuntimeException</code> with specified
079         * formatted detail message and nested <code>Throwable</code> root cause.
080         * Message formatting is delegated to {@link java.text.MessageFormat}.
081         * @param rootCause the exception or error that caused this exception
082         * to be thrown.
083         * @param pattern format specifier
084         * @param arguments format arguments
085         */
086        public MathRuntimeException(final Throwable rootCause,
087                                    final String pattern, final Object ... arguments) {
088            super(rootCause);
089            this.pattern   = pattern;
090            this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
091        }
092    
093        /**
094         * Translate a string to a given locale.
095         * @param s string to translate
096         * @param locale locale into which to translate the string
097         * @return translated string or original string
098         * for unsupported locales or unknown strings
099         */
100        private static String translate(final String s, final Locale locale) {
101            try {
102                ResourceBundle bundle =
103                        ResourceBundle.getBundle("org.apache.commons.math.MessagesResources", locale);
104                if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
105                    // the value of the resource is the translated string
106                    return bundle.getString(s);
107                }
108    
109            } catch (MissingResourceException mre) {
110                // do nothing here
111            }
112    
113            // the locale is not supported or the resource is unknown
114            // don't translate and fall back to using the string as is
115            return s;
116    
117        }
118    
119        /**
120         * Builds a message string by from a pattern and its arguments.
121         * @param locale Locale in which the message should be translated
122         * @param pattern format specifier
123         * @param arguments format arguments
124         * @return a message string
125         */
126        private static String buildMessage(final Locale locale, final String pattern,
127                                           final Object ... arguments) {
128            return (pattern == null) ? "" : new MessageFormat(translate(pattern, locale), locale).format(arguments);
129        }
130    
131        /** Gets the pattern used to build the message of this throwable.
132         *
133         * @return the pattern used to build the message of this throwable
134         */
135        public String getPattern() {
136            return pattern;
137        }
138    
139        /** Gets the arguments used to build the message of this throwable.
140         *
141         * @return the arguments used to build the message of this throwable
142         */
143        public Object[] getArguments() {
144            return arguments.clone();
145        }
146    
147        /** Gets the message in a specified locale.
148         *
149         * @param locale Locale in which the message should be translated
150         *
151         * @return localized message
152         */
153        public String getMessage(final Locale locale) {
154            return buildMessage(locale, pattern, arguments);
155        }
156    
157        /** {@inheritDoc} */
158        @Override
159        public String getMessage() {
160            return getMessage(Locale.US);
161        }
162    
163        /** {@inheritDoc} */
164        @Override
165        public String getLocalizedMessage() {
166            return getMessage(Locale.getDefault());
167        }
168    
169        /**
170         * Prints the stack trace of this exception to the standard error stream.
171         */
172        @Override
173        public void printStackTrace() {
174            printStackTrace(System.err);
175        }
176    
177        /**
178         * Prints the stack trace of this exception to the specified stream.
179         *
180         * @param out  the <code>PrintStream</code> to use for output
181         */
182        @Override
183        public void printStackTrace(final PrintStream out) {
184            synchronized (out) {
185                PrintWriter pw = new PrintWriter(out, false);
186                printStackTrace(pw);
187                // Flush the PrintWriter before it's GC'ed.
188                pw.flush();
189            }
190        }
191    
192        /**
193         * Constructs a new <code>ArithmeticException</code> with specified formatted detail message.
194         * Message formatting is delegated to {@link java.text.MessageFormat}.
195         * @param pattern format specifier
196         * @param arguments format arguments
197         * @return built exception
198         */
199        public static ArithmeticException createArithmeticException(final String pattern,
200                                                                    final Object ... arguments) {
201            return new ArithmeticException() {
202    
203                /** Serializable version identifier. */
204                private static final long serialVersionUID = 7705628723242533939L;
205    
206                /** {@inheritDoc} */
207                @Override
208                public String getMessage() {
209                    return buildMessage(Locale.US, pattern, arguments);
210                }
211    
212                /** {@inheritDoc} */
213                @Override
214                public String getLocalizedMessage() {
215                    return buildMessage(Locale.getDefault(), pattern, arguments);
216                }
217    
218            };
219        }
220    
221        /**
222         * Constructs a new <code>ArrayIndexOutOfBoundsException</code> with specified formatted detail message.
223         * Message formatting is delegated to {@link java.text.MessageFormat}.
224         * @param pattern format specifier
225         * @param arguments format arguments
226         * @return built exception
227         */
228        public static ArrayIndexOutOfBoundsException createArrayIndexOutOfBoundsException(final String pattern,
229                                                                                          final Object ... arguments) {
230            return new ArrayIndexOutOfBoundsException() {
231    
232                /** Serializable version identifier. */
233                private static final long serialVersionUID = -3394748305449283486L;
234    
235                /** {@inheritDoc} */
236                @Override
237                public String getMessage() {
238                    return buildMessage(Locale.US, pattern, arguments);
239                }
240    
241                /** {@inheritDoc} */
242                @Override
243                public String getLocalizedMessage() {
244                    return buildMessage(Locale.getDefault(), pattern, arguments);
245                }
246    
247            };
248        }
249    
250        /**
251         * Constructs a new <code>EOFException</code> with specified formatted detail message.
252         * Message formatting is delegated to {@link java.text.MessageFormat}.
253         * @param pattern format specifier
254         * @param arguments format arguments
255         * @return built exception
256         */
257        public static EOFException createEOFException(final String pattern,
258                                                      final Object ... arguments) {
259            return new EOFException() {
260    
261                /** Serializable version identifier. */
262                private static final long serialVersionUID = 279461544586092584L;
263    
264                /** {@inheritDoc} */
265                @Override
266                public String getMessage() {
267                    return buildMessage(Locale.US, pattern, arguments);
268                }
269    
270                /** {@inheritDoc} */
271                @Override
272                public String getLocalizedMessage() {
273                    return buildMessage(Locale.getDefault(), pattern, arguments);
274                }
275    
276            };
277        }
278    
279        /**
280         * Constructs a new <code>IOException</code> with specified nested
281         * <code>Throwable</code> root cause.
282         * <p>This factory method allows chaining of other exceptions within an
283         * <code>IOException</code> even for Java 5. The constructor for
284         * <code>IOException</code> with a cause parameter was introduced only
285         * with Java 6.</p>
286         * @param rootCause the exception or error that caused this exception
287         * to be thrown.
288         * @return built exception
289         */
290        public static IOException createIOException(final Throwable rootCause) {
291            IOException ioe = new IOException(rootCause.getLocalizedMessage());
292            ioe.initCause(rootCause);
293            return ioe;
294        }
295    
296        /**
297         * Constructs a new <code>IllegalArgumentException</code> with specified formatted detail message.
298         * Message formatting is delegated to {@link java.text.MessageFormat}.
299         * @param pattern format specifier
300         * @param arguments format arguments
301         * @return built exception
302         */
303        public static IllegalArgumentException createIllegalArgumentException(final String pattern,
304                                                                              final Object ... arguments) {
305            return new IllegalArgumentException() {
306    
307                /** Serializable version identifier. */
308                private static final long serialVersionUID = -6555453980658317913L;
309    
310                /** {@inheritDoc} */
311                @Override
312                public String getMessage() {
313                    return buildMessage(Locale.US, pattern, arguments);
314                }
315    
316                /** {@inheritDoc} */
317                @Override
318                public String getLocalizedMessage() {
319                    return buildMessage(Locale.getDefault(), pattern, arguments);
320                }
321    
322            };
323        }
324    
325        /**
326         * Constructs a new <code>IllegalArgumentException</code> with specified nested
327         * <code>Throwable</code> root cause.
328         * @param rootCause the exception or error that caused this exception
329         * to be thrown.
330         * @return built exception
331         */
332        public static IllegalArgumentException createIllegalArgumentException(final Throwable rootCause) {
333            IllegalArgumentException iae = new IllegalArgumentException(rootCause.getLocalizedMessage());
334            iae.initCause(rootCause);
335            return iae;
336        }
337    
338        /**
339         * Constructs a new <code>IllegalStateException</code> with specified formatted detail message.
340         * Message formatting is delegated to {@link java.text.MessageFormat}.
341         * @param pattern format specifier
342         * @param arguments format arguments
343         * @return built exception
344         */
345        public static IllegalStateException createIllegalStateException(final String pattern,
346                                                                        final Object ... arguments) {
347            return new IllegalStateException() {
348    
349                /** Serializable version identifier. */
350                private static final long serialVersionUID = -95247648156277208L;
351    
352                /** {@inheritDoc} */
353                @Override
354                public String getMessage() {
355                    return buildMessage(Locale.US, pattern, arguments);
356                }
357    
358                /** {@inheritDoc} */
359                @Override
360                public String getLocalizedMessage() {
361                    return buildMessage(Locale.getDefault(), pattern, arguments);
362                }
363    
364            };
365        }
366    
367        /**
368         * Constructs a new <code>ConcurrentModificationException</code> with specified formatted detail message.
369         * Message formatting is delegated to {@link java.text.MessageFormat}.
370         * @param pattern format specifier
371         * @param arguments format arguments
372         * @return built exception
373         */
374        public static ConcurrentModificationException createConcurrentModificationException(final String pattern,
375                                                                                            final Object ... arguments) {
376            return new ConcurrentModificationException() {
377    
378                /** Serializable version identifier. */
379                private static final long serialVersionUID = 6134247282754009421L;
380    
381                /** {@inheritDoc} */
382                @Override
383                public String getMessage() {
384                    return buildMessage(Locale.US, pattern, arguments);
385                }
386    
387                /** {@inheritDoc} */
388                @Override
389                public String getLocalizedMessage() {
390                    return buildMessage(Locale.getDefault(), pattern, arguments);
391                }
392    
393            };
394        }
395    
396        /**
397         * Constructs a new <code>NoSuchElementException</code> with specified formatted detail message.
398         * Message formatting is delegated to {@link java.text.MessageFormat}.
399         * @param pattern format specifier
400         * @param arguments format arguments
401         * @return built exception
402         */
403        public static NoSuchElementException createNoSuchElementException(final String pattern,
404                                                                          final Object ... arguments) {
405            return new NoSuchElementException() {
406    
407                /** Serializable version identifier. */
408                private static final long serialVersionUID = 7304273322489425799L;
409    
410                /** {@inheritDoc} */
411                @Override
412                public String getMessage() {
413                    return buildMessage(Locale.US, pattern, arguments);
414                }
415    
416                /** {@inheritDoc} */
417                @Override
418                public String getLocalizedMessage() {
419                    return buildMessage(Locale.getDefault(), pattern, arguments);
420                }
421    
422            };
423        }
424    
425        /**
426         * Constructs a new <code>NullPointerException</code> with specified formatted detail message.
427         * Message formatting is delegated to {@link java.text.MessageFormat}.
428         * @param pattern format specifier
429         * @param arguments format arguments
430         * @return built exception
431         */
432        public static NullPointerException createNullPointerException(final String pattern,
433                                                                      final Object ... arguments) {
434            return new NullPointerException() {
435    
436                /** Serializable version identifier. */
437                private static final long serialVersionUID = -3075660477939965216L;
438    
439                /** {@inheritDoc} */
440                @Override
441                public String getMessage() {
442                    return buildMessage(Locale.US, pattern, arguments);
443                }
444    
445                /** {@inheritDoc} */
446                @Override
447                public String getLocalizedMessage() {
448                    return buildMessage(Locale.getDefault(), pattern, arguments);
449                }
450    
451            };
452        }
453    
454       /**
455         * Constructs a new <code>ParseException</code> with specified
456         * formatted detail message.
457         * Message formatting is delegated to {@link java.text.MessageFormat}.
458         * @param offset offset at which error occurred
459         * @param pattern format specifier
460         * @param arguments format arguments
461         * @return built exception
462         */
463        public static ParseException createParseException(final int offset,
464                                                          final String pattern,
465                                                          final Object ... arguments) {
466            return new ParseException(null, offset) {
467    
468                /** Serializable version identifier. */
469                private static final long serialVersionUID = -1103502177342465975L;
470    
471                /** {@inheritDoc} */
472                @Override
473                public String getMessage() {
474                    return buildMessage(Locale.US, pattern, arguments);
475                }
476    
477                /** {@inheritDoc} */
478                @Override
479                public String getLocalizedMessage() {
480                    return buildMessage(Locale.getDefault(), pattern, arguments);
481                }
482    
483            };
484        }
485    
486        /** Create an {@link java.lang.RuntimeException} for an internal error.
487         * @param cause underlying cause
488         * @return an {@link java.lang.RuntimeException} for an internal error
489         */
490        public static RuntimeException createInternalError(final Throwable cause) {
491    
492            final String pattern  = "internal error, please fill a bug report at {0}";
493            final String argument = "https://issues.apache.org/jira/browse/MATH";
494    
495            return new RuntimeException() {
496    
497                /** Serializable version identifier. */
498                private static final long serialVersionUID = -201865440834027016L;
499    
500                /** {@inheritDoc} */
501                @Override
502                public String getMessage() {
503                    return buildMessage(Locale.US, pattern, argument);
504                }
505    
506                /** {@inheritDoc} */
507                @Override
508                public String getLocalizedMessage() {
509                    return buildMessage(Locale.getDefault(), pattern, argument);
510                }
511    
512            };
513    
514        }
515    
516    }