/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.io;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* An {@link OutputStream} that writes to multiple downstream
* {@link OutputStream}s.
*/
public class MultiplexOutputStream extends OutputStream {
@SuppressWarnings("serial")
public static final class DownstreamException extends IOException {
/**
*
*/
private static final long serialVersionUID = -688964594382950775L;
/**
* Print our stack trace as a cause for the specified stack trace.
*/
private static void printStackTraceAsCause(PrintStream s,
Throwable cause, StackTraceElement[] causedTrace, String tabs) {
// assert Thread.holdsLock(s);
// Compute number of frames in common between this and caused
StackTraceElement[] trace = cause.getStackTrace();
int m = trace.length - 1, n = causedTrace.length - 1;
while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
m--;
n--;
}
int framesInCommon = trace.length - 1 - m;
s.println(tabs + "Caused by: " + cause);
for (int i = 0; i <= m; i++) {
s.println(tabs + "\tat " + trace[i]);
}
if (framesInCommon != 0) {
s.println(tabs + "\t... " + framesInCommon + " more");
}
// Recurse if we have a cause
Throwable ourCause = cause.getCause();
if (ourCause != null) {
printStackTraceAsCause(s, ourCause, trace, tabs + " ");
}
}
/**
* Print our stack trace as a cause for the specified stack trace.
*/
private static void printStackTraceAsCause(PrintWriter s,
Throwable cause, StackTraceElement[] causedTrace, String tabs) {
// assert Thread.holdsLock(s);
// Compute number of frames in common between this and caused
StackTraceElement[] trace = cause.getStackTrace();
int m = trace.length - 1, n = causedTrace.length - 1;
while (m >= 0 && n >= 0 && trace[m].equals(causedTrace[n])) {
m--;
n--;
}
int framesInCommon = trace.length - 1 - m;
s.println(tabs + "Caused by: " + cause);
for (int i = 0; i <= m; i++) {
s.println(tabs + "\tat " + trace[i]);
}
if (framesInCommon != 0) {
s.println(tabs + "\t... " + framesInCommon + " more");
}
// Recurse if we have a cause
Throwable ourCause = cause.getCause();
if (ourCause != null) {
printStackTraceAsCause(s, cause, trace, tabs + " ");
}
}
private final List<Throwable> causes;
private DownstreamException(List<Throwable> causes) {
this.causes = Collections.unmodifiableList(causes);
}
public List<Throwable> getCauses() {
return causes;
}
/**
* Prints this throwable and its backtrace to the specified print
* stream.
*
* @param s
* <code>PrintStream</code> to use for output
*/
@Override
public void printStackTrace(PrintStream s) {
synchronized (s) {
s.println(this);
StackTraceElement[] trace = getStackTrace();
for (int i = 0; i < trace.length; i++) {
s.println("\tat " + trace[i]);
}
for (Throwable cause : causes) {
printStackTraceAsCause(s, cause, trace, " ");
}
}
}
/**
* Prints this throwable and its backtrace to the specified print
* writer.
*
* @param s
* <code>PrintWriter</code> to use for output
* @since JDK1.1
*/
@Override
public void printStackTrace(PrintWriter s) {
synchronized (s) {
s.println(this);
StackTraceElement[] trace = getStackTrace();
for (int i = 0; i < trace.length; i++) {
s.println("\tat " + trace[i]);
}
for (Throwable cause : causes) {
printStackTraceAsCause(s, cause, trace, " ");
}
}
}
}
OutputStream[] downstreams;
public MultiplexOutputStream(OutputStream... downstreams) {
this.downstreams = Arrays.copyOf(downstreams, downstreams.length);
}
@Override
public void close() throws IOException {
ArrayList<Throwable> downstreamExceptions = null;
for (OutputStream target : downstreams) {
try {
target.close();
} catch (Throwable t) {
if (downstreamExceptions == null) {
downstreamExceptions = new ArrayList<Throwable>();
}
downstreamExceptions.add(t);
}
}
if (downstreamExceptions != null) {
downstreamExceptions.trimToSize();
throw new DownstreamException(downstreamExceptions);
}
}
@Override
public void flush() throws IOException {
ArrayList<Throwable> downstreamExceptions = null;
for (OutputStream target : downstreams) {
try {
target.flush();
} catch (Throwable t) {
if (downstreamExceptions == null) {
downstreamExceptions = new ArrayList<Throwable>();
}
downstreamExceptions.add(t);
}
}
if (downstreamExceptions != null) {
downstreamExceptions.trimToSize();
throw new DownstreamException(downstreamExceptions);
}
}
@Override
public void write(int b) throws IOException {
ArrayList<Throwable> downstreamExceptions = null;
for (OutputStream target : downstreams) {
try {
target.write(b);
} catch (Throwable t) {
if (downstreamExceptions == null) {
downstreamExceptions = new ArrayList<Throwable>();
}
downstreamExceptions.add(t);
}
}
if (downstreamExceptions != null) {
downstreamExceptions.trimToSize();
throw new DownstreamException(downstreamExceptions);
}
}
}