All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quinsoft.zeidon.ZeidonException Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
    This file is part of the Zeidon Java Object Engine (Zeidon JOE).

    Zeidon JOE is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zeidon JOE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Zeidon JOE.  If not, see .

    Copyright 2009-2015 QuinSoft
 */
package com.quinsoft.zeidon;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.quinsoft.zeidon.objectdefinition.AttributeDef;
import com.quinsoft.zeidon.objectdefinition.DataRecord;
import com.quinsoft.zeidon.objectdefinition.EntityDef;
import com.quinsoft.zeidon.objectdefinition.LodDef;

/**
 * The base exception class for all Zeidon-specific exceptions.  This class has the following
 * conveniences over regular exceptions:
 *
 * - The exception message can be initialized using String.format parsing.  For example,
 *      new ZeidonException( "Line number: %d, text = %s", line, msg );
 *
 * - Message text can be added to the exception after it's been instantiated.  This allows
 *   code to catch an exception, add more information, and rethrow it.  See appendMessage()
 *   and prependMessage().
 *
 * - The logic is smart enough to ignore duplicate messages.
 *
 * - Checked exceptions can be easily wrapped by ZeidonException using wrapException().  This
 *   will keep the call stack the same as the wrapped exception.
 *
 * @author DG
 *
 */
public class ZeidonException extends RuntimeException
{
    private static final long serialVersionUID = 1L;

    public interface ExceptionHandler
    {
        void handleException( ZeidonException e );
    }

    /**
     * If set, this runnable is called every time a Zeidon Exception is created.
     */
    private static volatile ExceptionHandler exceptionHandler = null;

    private final String       message;
    private final List additionalInfo = new ArrayList();

    public ZeidonException( String format, Object...strings )
    {
        super(format( format, strings ));
        this.message = getClass().getName() + ": " + format( format, strings );
        runHandler();
    }

    /**
     * This is private because we expect outside code to wrap exceptions using wrapException or addMessage.
     *
     * @param t
     * @param message
     */
    private ZeidonException( Throwable t, String message )
    {
        super(message, t);
        this.message = t.toString();
        runHandler();
    }

    @Override
    public String getMessage()
    {
        return toString();
    }

    private void runHandler()
    {
        if ( exceptionHandler != null )
            exceptionHandler.handleException( this );
    }

    public ZeidonException addMessage( int position, String msg )
    {
        if ( additionalInfo.contains( msg ) )
            return this;

        // Validate the position.  Since this only gets called in the middle of exception
        // handling we don't want to throw an exception so we'll just assume values and
        // keep going.
        if ( position < 0 )
            position = 0;
        else
        if ( position > additionalInfo.size() )
            position = additionalInfo.size();

        additionalInfo.add( position, msg );
        return this;
    }

    /**
     * Adds additional text information to the exception message.  New messages are inserted at
     * the beginning of the "additional info".
     *
     * @param format
     * @param strings
     */
    public ZeidonException prependMessage( String format, Object...strings )
    {
        return addMessage( 0, format( format, strings ) );
    }

    /**
     * Adds additional text information to the exception message.  New messages are inserted at
     * the end of the "additional info".
     *
     * @param format
     * @param strings
     */
    public ZeidonException appendMessage( String format, Object...strings )
    {
        return addMessage( additionalInfo.size(), format( format, strings ) );
    }

    @Override
    public String toString()
    {
        return message + "\n" + StringUtils.join( additionalInfo, "\n" );
    }

    /**
     * Returns a ZeidonException with additional information added to the message of exception 't'.
     *
     * If 't' is a ZeidonException then the text is added and returned.
     * Otherwise a new ZeidonException is created that wraps the original exception and
     * attempts to keep the same message/stack trace.
     *
     * @param t
     * @param format
     * @param args
     * @return
     */
    public static ZeidonException prependMessage( Throwable t, String format, Object...args )
    {
        ZeidonException ze;
        if ( t instanceof ZeidonException )
            ze = (ZeidonException) t;
        else
            ze = wrapException( t );

        ze.prependMessage( format, args );
        return ze;
    }

    /**
     * Wraps 't' with a ZeidonException and changes the call stack for the ZeidonException to
     * match 't'.  This is mostly used to re-throw checked exceptions as unchecked ones.
     *
     * @param t
     * @return
     */
    public static ZeidonException wrapException( Throwable t )
    {
        // If this is already a ZeidonException then we don't need to wrap it.
        if ( t instanceof ZeidonException )
            return (ZeidonException) t;

        ZeidonException ze = new ZeidonException( t, t.toString() );
        ze.setStackTrace( t.getStackTrace() );
        return ze;
    }

    /**
     * Wrap String.format() so that we don't throw an additional exception.
     *
     * @param format
     * @param objs
     * @return
     */
    private static String format( String format, Object...objs )
    {
        try
        {
            if ( objs == null || objs.length == 0 )
                return format;

            return String.format( format, objs );
        }
        catch ( Exception e )
        {
            return format + "(Formatting error: " + e.getMessage() + ")";
        }
    }

    //
    // Convenience methods for formatting standard messages.  This will keep nested code
    // from adding the same message twice.
    //

    public ZeidonException prependAttributeDef( AttributeDef attributeDef )
    {
        return prependMessage( "AttributeDef = %s", attributeDef.toString() );
    }

    public ZeidonException prependEntityDef( EntityDef entityDef )
    {
        return prependMessage( "EntityDef  = %s", entityDef.toString() );
    }

    public ZeidonException prependLodDef( LodDef lodDef )
    {
        return prependMessage( "LodDef  = %s", lodDef.toString() );
    }

    public ZeidonException prependEntityInstance( EntityInstance entityInstance )
    {
        return prependMessage( "EI  = %s", entityInstance.toString() );
    }

    public ZeidonException prependFilename( String filename )
    {
        return prependMessage( "Filename  = %s", filename );
    }

    public ZeidonException prependDataRecord( DataRecord dataRecord )
    {
        return prependMessage( "Table = %s", dataRecord.getRecordName() );
    }

    public ZeidonException setCause( Throwable t )
    {
        initCause( t );
        return this;
    }

    public static ExceptionHandler getExceptionHandler()
    {
        return exceptionHandler;
    }

    public static void setExceptionHandler( ExceptionHandler exceptionHandler )
    {
        ZeidonException.exceptionHandler = exceptionHandler;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy