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

org.apache.commons.lang.exception.NestableDelegate Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.lang.exception;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * 

A shared implementation of the nestable exception functionality.

*

* The code is shared between * {@link org.apache.commons.lang.exception.NestableError NestableError}, * {@link org.apache.commons.lang.exception.NestableException NestableException} and * {@link org.apache.commons.lang.exception.NestableRuntimeException NestableRuntimeException}. *

* * @author Rafal Krzewski * @author Daniel L. Rall * @author Kasper Nielsen * @author Steven Caswell * @author Sean C. Sullivan * @author Stephen Colebourne * @since 1.0 * @version $Id: NestableDelegate.java 512889 2007-02-28 18:18:20Z dlr $ */ public class NestableDelegate implements Serializable { /** * Required for serialization support. * * @see java.io.Serializable */ private static final long serialVersionUID = 1L; /** * Constructor error message. */ private transient static final String MUST_BE_THROWABLE = "The Nestable implementation passed to the NestableDelegate(Nestable) " + "constructor must extend java.lang.Throwable"; /** * Holds the reference to the exception or error that we're * wrapping (which must be a {@link * org.apache.commons.lang.exception.Nestable} implementation). */ private Throwable nestable = null; /** * Whether to print the stack trace top-down. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v1.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.0 */ public static boolean topDown = true; /** * Whether to trim the repeated stack trace. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v1.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.0 */ public static boolean trimStackFrames = true; /** * Whether to match subclasses via indexOf. * This public flag may be set by calling code, typically in initialisation. * This exists for backwards compatability, setting it to false will return * the library to v2.0 behaviour (but will affect all users of the library * in the classloader). * @since 2.1 */ public static boolean matchSubclasses = true; /** * Constructs a new NestableDelegate instance to manage the * specified Nestable. * * @param nestable the Nestable implementation (must extend * {@link java.lang.Throwable}) * @since 2.0 */ public NestableDelegate(Nestable nestable) { if (nestable instanceof Throwable) { this.nestable = (Throwable) nestable; } else { throw new IllegalArgumentException(MUST_BE_THROWABLE); } } /** * Returns the error message of the Throwable in the chain of Throwables at the * specified index, numbered from 0. * * @param index * the index of the Throwable in the chain of Throwables * @return the error message, or null if the Throwable at the specified index in the chain does not * contain a message * @throws IndexOutOfBoundsException * if the index argument is negative or not less than the count of Throwables * in the chain * @since 2.0 */ public String getMessage(int index) { Throwable t = this.getThrowable(index); if (Nestable.class.isInstance(t)) { return ((Nestable) t).getMessage(0); } return t.getMessage(); } /** * Returns the full message contained by the Nestable and any nested Throwables. * * @param baseMsg * the base message to use when creating the full message. Should be generally be called via * nestableHelper.getMessage(super.getMessage()), where super is an * instance of {@link java.lang.Throwable}. * @return The concatenated message for this and all nested Throwables * @since 2.0 */ public String getMessage(String baseMsg) { Throwable nestedCause = ExceptionUtils.getCause(this.nestable); String causeMsg = nestedCause == null ? null : nestedCause.getMessage(); if (nestedCause == null || causeMsg == null) { return baseMsg; // may be null, which is a valid result } if (baseMsg == null) { return causeMsg; } return baseMsg + ": " + causeMsg; } /** * Returns the error message of this and any nested Throwables in an array of Strings, one element * for each message. Any Throwable not containing a message is represented in the array by a null. * This has the effect of cause the length of the returned array to be equal to the result of the * {@link #getThrowableCount()} operation. * * @return the error messages * @since 2.0 */ public String[] getMessages() { Throwable[] throwables = this.getThrowables(); String[] msgs = new String[throwables.length]; for (int i = 0; i < throwables.length; i++) { msgs[i] = (Nestable.class.isInstance(throwables[i]) ? ((Nestable) throwables[i]).getMessage(0) : throwables[i].getMessage()); } return msgs; } /** * Returns the Throwable in the chain of * Throwables at the specified index, numbered from 0. * * @param index the index, numbered from 0, of the Throwable in * the chain of Throwables * @return the Throwable * @throws IndexOutOfBoundsException if the index argument is * negative or not less than the count of Throwables in the * chain * @since 2.0 */ public Throwable getThrowable(int index) { if (index == 0) { return this.nestable; } Throwable[] throwables = this.getThrowables(); return throwables[index]; } /** * Returns the number of Throwables contained in the * Nestable contained by this delegate. * * @return the throwable count * @since 2.0 */ public int getThrowableCount() { return ExceptionUtils.getThrowableCount(this.nestable); } /** * Returns this delegate's Nestable and any nested * Throwables in an array of Throwables, one * element for each Throwable. * * @return the Throwables * @since 2.0 */ public Throwable[] getThrowables() { return ExceptionUtils.getThrowables(this.nestable); } /** * Returns the index, numbered from 0, of the first Throwable * that matches the specified type, or a subclass, in the chain of Throwables * with an index greater than or equal to the specified index. * The method returns -1 if the specified type is not found in the chain. *

* NOTE: From v2.1, we have clarified the Nestable interface * such that this method matches subclasses. * If you want to NOT match subclasses, please use * {@link ExceptionUtils#indexOfThrowable(Throwable, Class, int)} * (which is avaiable in all versions of lang). * An alternative is to use the public static flag {@link #matchSubclasses} * on NestableDelegate, however this is not recommended. * * @param type the type to find, subclasses match, null returns -1 * @param fromIndex the index, numbered from 0, of the starting position in * the chain to be searched * @return index of the first occurrence of the type in the chain, or -1 if * the type is not found * @throws IndexOutOfBoundsException if the fromIndex argument * is negative or not less than the count of Throwables in the * chain * @since 2.0 */ public int indexOfThrowable(Class type, int fromIndex) { if (type == null) { return -1; } if (fromIndex < 0) { throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex); } Throwable[] throwables = ExceptionUtils.getThrowables(this.nestable); if (fromIndex >= throwables.length) { throw new IndexOutOfBoundsException("The start index was out of bounds: " + fromIndex + " >= " + throwables.length); } if (matchSubclasses) { for (int i = fromIndex; i < throwables.length; i++) { if (type.isAssignableFrom(throwables[i].getClass())) { return i; } } } else { for (int i = fromIndex; i < throwables.length; i++) { if (type.equals(throwables[i].getClass())) { return i; } } } return -1; } /** * Prints the stack trace of this exception the the standar error * stream. */ public void printStackTrace() { printStackTrace(System.err); } /** * Prints the stack trace of this exception to the specified * stream. * * @param out PrintStream to use for output. * @see #printStackTrace(PrintWriter) */ public void printStackTrace(PrintStream out) { synchronized (out) { PrintWriter pw = new PrintWriter(out, false); printStackTrace(pw); // Flush the PrintWriter before it's GC'ed. pw.flush(); } } /** * Prints the stack trace of this exception to the specified * writer. If the Throwable class has a getCause * method (i.e. running on jre1.4 or higher), this method just * uses Throwable's printStackTrace() method. Otherwise, generates * the stack-trace, by taking into account the 'topDown' and * 'trimStackFrames' parameters. The topDown and trimStackFrames * are set to 'true' by default (produces jre1.4-like stack trace). * * @param out PrintWriter to use for output. */ public void printStackTrace(PrintWriter out) { Throwable throwable = this.nestable; // if running on jre1.4 or higher, use default printStackTrace if (ExceptionUtils.isThrowableNested()) { if (throwable instanceof Nestable) { ((Nestable)throwable).printPartialStackTrace(out); } else { throwable.printStackTrace(out); } return; } // generating the nested stack trace List stacks = new ArrayList(); while (throwable != null) { String[] st = getStackFrames(throwable); stacks.add(st); throwable = ExceptionUtils.getCause(throwable); } // If NOT topDown, reverse the stack String separatorLine = "Caused by: "; if (!topDown) { separatorLine = "Rethrown as: "; Collections.reverse(stacks); } // Remove the repeated lines in the stack if (trimStackFrames) { trimStackFrames(stacks); } synchronized (out) { for (Iterator iter=stacks.iterator(); iter.hasNext();) { String[] st = (String[]) iter.next(); for (int i=0, len=st.length; i < len; i++) { out.println(st[i]); } if (iter.hasNext()) { out.print(separatorLine); } } } } /** * Captures the stack trace associated with the specified * Throwable object, decomposing it into a list of * stack frames. * * @param t The Throwable. * @return An array of strings describing each stack frame. * @since 2.0 */ protected String[] getStackFrames(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); // Avoid infinite loop between decompose() and printStackTrace(). if (t instanceof Nestable) { ((Nestable) t).printPartialStackTrace(pw); } else { t.printStackTrace(pw); } return ExceptionUtils.getStackFrames(sw.getBuffer().toString()); } /** * Trims the stack frames. The first set is left untouched. The rest * of the frames are truncated from the bottom by comparing with * one just on top. * * @param stacks The list containing String[] elements * @since 2.0 */ protected void trimStackFrames(List stacks) { for (int size=stacks.size(), i=size-1; i > 0; i--) { String[] curr = (String[]) stacks.get(i); String[] next = (String[]) stacks.get(i-1); List currList = new ArrayList(Arrays.asList(curr)); List nextList = new ArrayList(Arrays.asList(next)); ExceptionUtils.removeCommonFrames(currList, nextList); int trimmed = curr.length - currList.size(); if (trimmed > 0) { currList.add("\t... "+trimmed+" more"); stacks.set( i, currList.toArray(new String[currList.size()]) ); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy