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.PrintStream;
020    import java.io.PrintWriter;
021    import java.text.MessageFormat;
022    import java.util.Locale;
023    import java.util.MissingResourceException;
024    import java.util.ResourceBundle;
025    
026    
027    /**
028    * Base class for commons-math checked exceptions.
029    * <p>
030    * Supports nesting, emulating JDK 1.4 behavior if necessary.</p>
031    * <p>
032    * Adapted from <a href="http://commons.apache.org/collections/api-release/org/apache/commons/collections/FunctorException.html"/>.</p>
033    * 
034    * @version $Revision: 780674 $ $Date: 2009-06-01 11:10:55 -0400 (Mon, 01 Jun 2009) $
035    */
036    public class MathException extends Exception {
037        
038        /** Serializable version identifier. */
039        private static final long serialVersionUID = -9004610152740737812L;
040    
041        /**
042         * Pattern used to build the message.
043         */
044        private final String pattern;
045    
046        /**
047         * Arguments used to build the message.
048         */
049        private final Object[] arguments;
050    
051        /**
052         * Translate a string to a given locale.
053         * @param s string to translate
054         * @param locale locale into which to translate the string
055         * @return translated string or original string
056         * for unsupported locales or unknown strings
057         */
058        private static String translate(String s, Locale locale) {
059            try {
060                ResourceBundle bundle =
061                        ResourceBundle.getBundle("org.apache.commons.math.MessagesResources", locale);
062                if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
063                    // the value of the resource is the translated string
064                    return bundle.getString(s);
065                }
066                
067            } catch (MissingResourceException mre) {
068                // do nothing here
069            }
070    
071            // the locale is not supported or the resource is unknown
072            // don't translate and fall back to using the string as is
073            return s;
074    
075        }
076    
077        /**
078         * Builds a message string by from a pattern and its arguments.
079         * @param locale Locale in which the message should be translated
080         * @param pattern format specifier
081         * @param arguments format arguments
082         * @return a message string
083         */
084        private static String buildMessage(Locale locale, String pattern, Object ... arguments) {
085            return (pattern == null) ? "" : new MessageFormat(translate(pattern, locale), locale).format(arguments);        
086        }
087    
088        /**
089         * Constructs a new <code>MathException</code> with no
090         * detail message.
091         */
092        public MathException() {
093            super();
094            this.pattern   = null;
095            this.arguments = new Object[0];
096        }
097        
098        /**
099         * Constructs a new <code>MathException</code> with specified
100         * formatted detail message.
101         * Message formatting is delegated to {@link java.text.MessageFormat}.
102         * @param pattern format specifier
103         * @param arguments format arguments
104         */
105        public MathException(String pattern, Object ... arguments) {
106          super(buildMessage(Locale.US, pattern, arguments));
107          this.pattern   = pattern;
108          this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
109        }
110    
111        /**
112         * Constructs a new <code>MathException</code> with specified
113         * nested <code>Throwable</code> root cause.
114         *
115         * @param rootCause  the exception or error that caused this exception
116         *                   to be thrown.
117         */
118        public MathException(Throwable rootCause) {
119            super(rootCause);
120            this.pattern   = getMessage();
121            this.arguments = new Object[0];
122        }
123        
124        /**
125         * Constructs a new <code>MathException</code> with specified
126         * formatted detail message and nested <code>Throwable</code> root cause.
127         * Message formatting is delegated to {@link java.text.MessageFormat}.
128         * @param rootCause the exception or error that caused this exception
129         * to be thrown.
130         * @param pattern format specifier
131         * @param arguments format arguments
132         * @since 1.2
133         */
134        public MathException(Throwable rootCause, String pattern, Object ... arguments) {
135          super(buildMessage(Locale.US, pattern, arguments), rootCause);
136          this.pattern   = pattern;
137          this.arguments = (arguments == null) ? new Object[0] : arguments.clone();
138        }
139    
140        /** Gets the pattern used to build the message of this throwable.
141         *
142         * @return the pattern used to build the message of this throwable
143         * @since 1.2
144         */
145        public String getPattern() {
146            return pattern;
147        }
148    
149        /** Gets the arguments used to build the message of this throwable.
150         *
151         * @return the arguments used to build the message of this throwable
152         * @since 1.2
153         */
154        public Object[] getArguments() {
155            return arguments.clone();
156        }
157    
158        /** Gets the message in a specified locale.
159         *
160         * @param locale Locale in which the message should be translated
161         * 
162         * @return localized message
163         * @since 1.2
164         */
165        public String getMessage(Locale locale) {
166            return buildMessage(locale, pattern, arguments);
167        }
168    
169        /** {@inheritDoc} */
170        @Override
171        public String getLocalizedMessage() {
172            return getMessage(Locale.getDefault());
173        }
174    
175        /**
176         * Prints the stack trace of this exception to the standard error stream.
177         */
178        @Override
179        public void printStackTrace() {
180            printStackTrace(System.err);
181        }
182        
183        /**
184         * Prints the stack trace of this exception to the specified stream.
185         *
186         * @param out  the <code>PrintStream</code> to use for output
187         */
188        @Override
189        public void printStackTrace(PrintStream out) {
190            synchronized (out) {
191                PrintWriter pw = new PrintWriter(out, false);
192                printStackTrace(pw);
193                // Flush the PrintWriter before it's GC'ed.
194                pw.flush();
195            }
196        }
197    
198    }