/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library 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 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.util;
import java.io.Closeable;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.apache.log4j.Logger;
/**
* Common utility code for higher-level operations on IO streams. Notwithstanding the
* nominal access for the class and its methods, user (command) code should avoid using
* this class directly. You should program against the
* {@link org.jnode.shell.io.CommandIO} API instead.
*
* @author crawley@jnode.org
*/
public class IOUtils {
// FIXME ... these utils (in some cases) attempt to access non-public fields
// of various stream classes in order to figure out what the underlying stream
// is. Currently, we have to explicitly grant the calling application permissions
// to do this (e.g. via the plugin descriptor). Ideally, this should be unnecessary
// ... but I cannot figure out how to implement this.
private IOUtils() {
// Prevent instantiation
}
public static boolean isTTY(Closeable stream) {
if (stream instanceof ConsoleStream) {
return true;
} else if (stream instanceof ProxyStream<?>) {
return isTTY(((ProxyStream<?>) stream).getRealStream());
} else if (stream instanceof OutputStreamWriter) {
return isTTY(findOutputStream((OutputStreamWriter) stream));
} else if (stream instanceof InputStreamReader) {
return isTTY(findInputStream((InputStreamReader) stream));
} else if (stream instanceof ReaderInputStream) {
return isTTY(((ReaderInputStream) stream).getReader());
} else if (stream instanceof WriterOutputStream) {
return isTTY(((WriterOutputStream) stream).getWriter());
} else if (stream instanceof FilterInputStream) {
return isTTY(findInputStream((FilterInputStream) stream));
} else if (stream instanceof FilterOutputStream) {
return isTTY(findOutputStream((FilterOutputStream) stream));
} else {
return false;
}
}
public static boolean isPipe(Closeable stream) {
if (stream instanceof PipeStream) {
return true;
} else if (stream instanceof ProxyStream<?>) {
return isPipe(((ProxyStream<?>) stream).getRealStream());
} else if (stream instanceof OutputStreamWriter) {
return isPipe(findOutputStream((OutputStreamWriter) stream));
} else if (stream instanceof InputStreamReader) {
return isPipe(findInputStream((InputStreamReader) stream));
} else if (stream instanceof ReaderInputStream) {
return isPipe(((ReaderInputStream) stream).getReader());
} else if (stream instanceof WriterOutputStream) {
return isPipe(((WriterOutputStream) stream).getWriter());
} else if (stream instanceof FilterInputStream) {
return isPipe(findInputStream((FilterInputStream) stream));
} else if (stream instanceof FilterOutputStream) {
return isPipe(findOutputStream((FilterOutputStream) stream));
} else {
return false;
}
}
public static Closeable findBaseStream(Closeable stream) {
if (stream instanceof ConsoleStream) {
return stream;
} else if (stream instanceof ProxyStream<?>) {
return findBaseStream(((ProxyStream<?>) stream).getRealStream());
} else if (stream instanceof OutputStreamWriter) {
return findBaseStream(findOutputStream((OutputStreamWriter) stream));
} else if (stream instanceof InputStreamReader) {
return findBaseStream(findInputStream((InputStreamReader) stream));
} else if (stream instanceof ReaderInputStream) {
return findBaseStream(((ReaderInputStream) stream).getReader());
} else if (stream instanceof WriterOutputStream) {
return findBaseStream(((WriterOutputStream) stream).getWriter());
} else if (stream instanceof FilterInputStream) {
return findBaseStream(findInputStream((FilterInputStream) stream));
} else if (stream instanceof FilterOutputStream) {
return findBaseStream(findOutputStream((FilterOutputStream) stream));
} else {
return stream;
}
}
private static InputStream findInputStream(final FilterInputStream inputStream) {
PrivilegedAction<InputStream> pa = new PrivilegedAction<InputStream>() {
public InputStream run() {
try {
Class<FilterInputStream> cls = FilterInputStream.class;
Field field = cls.getDeclaredField("in");
field.setAccessible(true);
Object in = field.get(inputStream);
field.setAccessible(false);
return (InputStream) in;
} catch (Exception ex) {
Logger.getLogger(IOUtils.class).error("Cannot extract the 'in' field", ex);
return null;
}
}
};
return AccessController.doPrivileged(pa);
}
private static OutputStream findOutputStream(final FilterOutputStream outputStream) {
PrivilegedAction<OutputStream> pa = new PrivilegedAction<OutputStream>() {
public OutputStream run() {
try {
Class<FilterOutputStream> cls = FilterOutputStream.class;
Field field = cls.getDeclaredField("out");
field.setAccessible(true);
Object out = field.get(outputStream);
return (OutputStream) out;
} catch (Exception ex) {
Logger.getLogger(IOUtils.class).error("Cannot extract the 'out' field", ex);
return null;
}
}
};
return AccessController.doPrivileged(pa);
}
private static OutputStream findOutputStream(final OutputStreamWriter writer) {
// This implementation is based on the knowledge that an OutputStreamWriter
// uses the underlying OutputStream as its 'lock' object.
PrivilegedAction<OutputStream> pa = new PrivilegedAction<OutputStream>() {
public OutputStream run() {
try {
Class<Writer> cls = Writer.class;
Field field = cls.getDeclaredField("lock");
field.setAccessible(true);
Object lock = field.get(writer);
return (OutputStream) lock;
} catch (Exception ex) {
Logger.getLogger(IOUtils.class).error("Cannot extract the 'lock' field", ex);
return null;
}
}
};
return AccessController.doPrivileged(pa);
}
private static InputStream findInputStream(final InputStreamReader reader) {
// This implementation is based on the knowledge that an InputStreamReader
// uses the underlying InputStream as its 'lock' object.
PrivilegedAction<InputStream> pa = new PrivilegedAction<InputStream>() {
public InputStream run() {
try {
Class<Reader> cls = Reader.class;
Field field = cls.getDeclaredField("lock");
field.setAccessible(true);
Object lock = field.get(reader);
return (InputStream) lock;
} catch (Exception ex) {
Logger.getLogger(IOUtils.class).error("Cannot extract the 'lock' field", ex);
return null;
}
}
};
return AccessController.doPrivileged(pa);
}
}