com.quinsoft.zeidon.ZeidonException.java Source code

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.ZeidonException.java

Source

/**
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 <http://www.gnu.org/licenses/>.
    
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<String> additionalInfo = new ArrayList<String>();

    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;
    }
}