001/*
002 *  jDTAUS Core Messages
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.core.messages;
022
023import java.io.InvalidObjectException;
024import java.io.ObjectStreamException;
025import java.util.Locale;
026import org.jdtaus.core.container.ContainerFactory;
027import org.jdtaus.core.text.Message;
028
029/**
030 * {@code Message} stating that an exception occured.
031 * <p>The {@code Throwable} passed to the constructor of this class is used to
032 * determine the message's format arguments. For chained exceptions, that is,
033 * {@code getCause()} returns a non-{@code null} value, the root cause is used.
034 * </p>
035 *
036 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
037 * @version $JDTAUS: ExceptionMessage.java 8788 2012-12-03 02:15:13Z schulte $
038 */
039public final class ExceptionMessage extends Message
040{
041    //--Constants---------------------------------------------------------------
042
043    /** Serial version UID for backwards compatibility with 1.0.x classes. */
044    private static final long serialVersionUID = 3771927969335656661L;
045
046    //---------------------------------------------------------------Constants--
047    //--ExceptionMessage--------------------------------------------------------
048
049    /**
050     * Throwable of the message.
051     * @serial
052     */
053    private final Throwable throwable;
054
055    /**
056     * Root cause to report.
057     * @serial
058     */
059    private Throwable rootCause;
060
061    /**
062     * Creates a new {@code ExceptionMessage} taking the {@code Throwable} to
063     * report.
064     *
065     * @param throwable the {@code Throwable} to report.
066     *
067     * @throws NullPointerException if {@code throwable} is {@code null}.
068     */
069    public ExceptionMessage( final Throwable throwable )
070    {
071        if ( throwable == null )
072        {
073            throw new NullPointerException( "throwable" );
074        }
075
076        Throwable current = throwable;
077        Throwable report = current;
078        while ( ( current = current.getCause() ) != null )
079        {
080            report = current;
081        }
082
083        this.throwable = throwable;
084        this.rootCause = report;
085    }
086
087    //--------------------------------------------------------ExceptionMessage--
088    //--Serializable------------------------------------------------------------
089
090    /**
091     * Takes care of initializing the {@code rootCause} field when constructed
092     * from an 1.0.x object stream.
093     *
094     * @throws ObjectStreamException if no root cause can be resolved.
095     */
096    private Object readResolve() throws ObjectStreamException
097    {
098        if ( this.throwable != null && this.rootCause == null )
099        {
100            Throwable current = this.throwable;
101            Throwable report = current;
102            while ( ( current = current.getCause() ) != null )
103            {
104                report = current;
105            }
106            this.rootCause = report;
107        }
108        else if ( !( this.throwable != null && this.rootCause != null ) )
109        {
110            throw new InvalidObjectException(
111                this.getMissingObjectStreamFieldMessage( this.getLocale() ) );
112
113        }
114
115        return this;
116    }
117
118    //------------------------------------------------------------Serializable--
119    //--Message-----------------------------------------------------------------
120
121    /**
122     * {@inheritDoc}
123     * <p>This method traverses up the chained hierarchy. The arguments
124     * returned are constructed using the root cause.</p>
125     *
126     * @return Strings describing the throwable.
127     * <ul>
128     * <li>[0]: the fully qualified classname of the root cause.</li>
129     * <li>[1]: the classname of the root cause without package.</li>
130     * <li>[2]: the message of the root cause.</li>
131     * </ul>
132     */
133    public Object[] getFormatArguments( final Locale locale )
134    {
135        String name = this.rootCause.getClass().getName();
136        final int lastDot = name.lastIndexOf( '.' );
137
138        if ( lastDot >= 0 )
139        {
140            name = name.substring( lastDot + 1 );
141        }
142
143        String message = this.rootCause.getMessage();
144        if ( message == null )
145        {
146            message = this.getNoDetailsAvailableMessage( locale );
147        }
148
149        return new Object[]
150            {
151                this.rootCause.getClass().getName(), name, message
152            };
153    }
154
155    /**
156     * {@inheritDoc}
157     *
158     * @return The corresponding text from the message's {@code ResourceBundle}:
159     * <blockquote><pre>
160     * An {1} occured.
161     * Details: {2}
162     * </pre></blockquote>
163     */
164    public String getText( final Locale locale )
165    {
166        String name = this.rootCause.getClass().getName();
167        final int lastDot = name.lastIndexOf( '.' );
168
169        if ( lastDot >= 0 )
170        {
171            name = name.substring( lastDot + 1 );
172        }
173
174        String message = this.rootCause.getMessage();
175        if ( message == null )
176        {
177            message = this.getNoDetailsAvailableMessage( locale );
178        }
179
180        String text;
181        if ( this.rootCause instanceof Error )
182        {
183            text = this.getErrorInfoMessage( locale, name, message );
184        }
185        else
186        {
187            text = this.getExceptionInfoMessage( locale, name, message );
188        }
189
190        return text;
191    }
192
193    //-----------------------------------------------------------------Message--
194    //--Dependencies------------------------------------------------------------
195
196// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
197    // This section is managed by jdtaus-container-mojo.
198
199    /**
200     * Gets the configured <code>Locale</code> implementation.
201     *
202     * @return The configured <code>Locale</code> implementation.
203     */
204    private Locale getLocale()
205    {
206        return (Locale) ContainerFactory.getContainer().
207            getDependency( this, "Locale" );
208
209    }
210
211// </editor-fold>//GEN-END:jdtausDependencies
212
213    //------------------------------------------------------------Dependencies--
214    //--Messages----------------------------------------------------------------
215
216// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
217    // This section is managed by jdtaus-container-mojo.
218
219    /**
220     * Gets the text of message <code>exceptionInfo</code>.
221     * <blockquote><pre>Eine {0} ist aufgetreten. {1}</pre></blockquote>
222     * <blockquote><pre>A {0} occured. {1}</pre></blockquote>
223     *
224     * @param locale The locale of the message instance to return.
225     * @param exceptionName Name of the exception.
226     * @param exceptionMessage Message of the exception.
227     *
228     * @return Information about an exception.
229     */
230    private String getExceptionInfoMessage( final Locale locale,
231            final java.lang.String exceptionName,
232            final java.lang.String exceptionMessage )
233    {
234        return ContainerFactory.getContainer().
235            getMessage( this, "exceptionInfo", locale,
236                new Object[]
237                {
238                    exceptionName,
239                    exceptionMessage
240                });
241
242    }
243
244    /**
245     * Gets the text of message <code>errorInfo</code>.
246     * <blockquote><pre>Ein {0} ist aufgetreten. {1}</pre></blockquote>
247     * <blockquote><pre>A {0} occured. {1}</pre></blockquote>
248     *
249     * @param locale The locale of the message instance to return.
250     * @param errorName Name of the error.
251     * @param errorMessage Message of the error.
252     *
253     * @return Information about an error.
254     */
255    private String getErrorInfoMessage( final Locale locale,
256            final java.lang.String errorName,
257            final java.lang.String errorMessage )
258    {
259        return ContainerFactory.getContainer().
260            getMessage( this, "errorInfo", locale,
261                new Object[]
262                {
263                    errorName,
264                    errorMessage
265                });
266
267    }
268
269    /**
270     * Gets the text of message <code>noDetailsAvailable</code>.
271     * <blockquote><pre>Keine Details verfügbar.</pre></blockquote>
272     * <blockquote><pre>No details available.</pre></blockquote>
273     *
274     * @param locale The locale of the message instance to return.
275     *
276     * @return Information that no details are available.
277     */
278    private String getNoDetailsAvailableMessage( final Locale locale )
279    {
280        return ContainerFactory.getContainer().
281            getMessage( this, "noDetailsAvailable", locale, null );
282
283    }
284
285    /**
286     * Gets the text of message <code>missingObjectStreamField</code>.
287     * <blockquote><pre>Fehlende Felder im Datenstrom.</pre></blockquote>
288     * <blockquote><pre>Missing fields in object stream.</pre></blockquote>
289     *
290     * @param locale The locale of the message instance to return.
291     *
292     * @return Information that a field in an object stream is missing.
293     */
294    private String getMissingObjectStreamFieldMessage( final Locale locale )
295    {
296        return ContainerFactory.getContainer().
297            getMessage( this, "missingObjectStreamField", locale, null );
298
299    }
300
301// </editor-fold>//GEN-END:jdtausMessages
302
303    //----------------------------------------------------------------Messages--
304}