001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 *  image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause 
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 
006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 
007 * Visit http://imagingbook.com for additional details.
008 *******************************************************************************/
009
010package imagingbook.lib.ij;
011
012import ij.IJ;
013import imagingbook.lib.ij.IjLogStream;
014import imagingbook.lib.ij.IjLogStream;
015import imagingbook.lib.ij.IjLogStream;
016
017import java.io.ByteArrayOutputStream;
018import java.io.PrintStream;
019
020/**
021 * This class provides the functionality to divert output sent to the System.out
022 * and System.err streams to ImageJ's log console. The purpose is to allow 
023 * use of existing Java classes or writing new generic Java classes that only 
024 * output to System.out and are thus less dependent on ImageJ.
025 *
026 * @author W. Burger
027 * @version 2015-05-04
028 */
029public class IjLogStream extends PrintStream {
030        
031        private static PrintStream orgSystemOut = null;
032        private static PrintStream orgSystemErr = null;
033        private static PrintStream tmpSystemOut = null;
034        private static PrintStream tmpSystemErr = null;
035        
036        /**
037         * Redirects all output sent to <code>System.out</code> and <code>System.err</code> 
038         * to ImageJ's log console using empty prefix strings.
039         * Alternatively use 
040         * {@link #redirectSystemOut(String)} and {@link #redirectSystemErr(String)}
041         * to redirect the streams separately and to specify individual prefix strings.
042         */
043        public static void redirectSystem() {
044                redirectSystem("", "");
045        }
046        
047        /**
048         * Redirects all output sent to <code>System.out</code> and <code>System.err</code> 
049         * to ImageJ's log console using the default prefixes.
050         * @param outPrefix The prefix string inserted at the start of each line sent to <code>System.out</code>.
051         * @param errPrefix The prefix string inserted at the start of each line sent to <code>System.err</code>.
052         */
053        public static void redirectSystem(String outPrefix, String errPrefix) {
054                redirectSystemOut(outPrefix);
055                redirectSystemErr(errPrefix);
056                //IJ.log("[redirectSystem] streams created");
057        }
058        
059        /**
060         * Redirects all output sent to <code>System.out</code> to ImageJ's log console.
061         * @param prefix The prefix string inserted at the start of each output line. 
062         * Pass <code>null</code> to use the default prefix or an empty string to 
063         * remove the prefix.
064         */
065        public static void redirectSystemOut(String prefix) {
066                if (orgSystemOut == null) {             // has no effect if System.out is already replaced
067                        orgSystemOut = System.out;              // remember the original System.out stream
068                        tmpSystemOut = new IjLogStream(prefix);
069                        System.setOut(tmpSystemOut);
070                }
071        }
072        
073        /**
074         * Redirects all output sent to <code>System.err</code> to ImageJ's log console.
075         * @param prefix The prefix string inserted at the start of each output line. 
076         * Pass <code>null</code>  to use the default prefix or an empty string to 
077         * remove the prefix.
078         */
079        public static void redirectSystemErr(String prefix) {
080                if (orgSystemErr == null) {             // has no effect if System.out is already replaced
081                        orgSystemErr = System.err;              // remember the original System.out stream
082                        tmpSystemErr = new IjLogStream(prefix);
083                        System.setErr(tmpSystemErr);
084                }
085        }
086        
087        /**
088         * Returns the redirection stream for {@code System.out} if it exists.
089         * Note that a reference to the current output stream can also be obtained directly from 
090         * the {@code System.out} field.
091         * @return A reference to the {@code PrintStream} object currently substituting {@code System.out}
092         * or {@code null} of if {@code System.out} is currently not redirected.
093         */
094        public static PrintStream getCurrentOutStream() {
095                return tmpSystemOut;
096        }
097        
098        /**
099         * Returns the redirection stream for {@code System.err} if it exists.
100         * Note that a reference to the current output stream can also be obtained directly from 
101         * the {@code System.err} field.
102         * @return A reference to the {@code PrintStream} object currently substituting {@code System.err}
103         * or {@code null} of if {@code System.err} is currently not redirected.
104         */
105        public static PrintStream getCurrentErrStream() {
106                return tmpSystemErr;
107        }
108
109        /**
110         * Use this method to revert both <code>System.out</code> and <code>System.err</code> 
111         * to their original output streams.
112         */
113        public static void revertSystem() {
114                revertSystemOut();
115                revertSystemErr();
116        }
117        
118        /**
119         * Use this method to revert<code>System.out</code>
120         * to the original output stream.
121         */
122        public static void revertSystemOut() {
123                if (orgSystemOut != null && tmpSystemOut != null) {
124                        tmpSystemOut.flush();
125                        tmpSystemOut.close();
126                        System.setOut(orgSystemOut);
127                        orgSystemOut = null;
128                        tmpSystemOut = null;
129                }
130        }
131        
132        /**
133         * Use this method to revert<code>System.err</code>
134         * to the original output stream.
135         */
136        public static void revertSystemErr() {
137                if (orgSystemErr != null && tmpSystemErr != null) {
138                        tmpSystemErr.flush();
139                        tmpSystemErr.close();
140                        System.setErr(orgSystemErr);
141                        orgSystemErr = null;
142                        tmpSystemErr = null;
143                }
144        }
145        
146        // ----------------------------------------------------------------
147        
148        private final String endOfLineSystem = System.getProperty("line.separator"); 
149        private final String endOfLineShort = String.format("\n");      
150        private final ByteArrayOutputStream byteStream;
151        private final String prefix;
152        
153        /**
154         * The only constructor, not to be used from outside (private).
155         * @param prefix The prefix string preceding every output line produced by this stream.
156         */
157        private IjLogStream(String prefix) {
158                super(new ByteArrayOutputStream());
159                this.byteStream = (ByteArrayOutputStream) this.out;
160                this.prefix = (prefix == null) ? "" : prefix;
161        }
162        
163        @Override
164        // ever called?
165        public void write(byte[] b) {
166                this.write(b, 0, b.length);
167        }
168        
169        @Override
170        public void write(byte[] b, int off, int len) {
171                String msg = new String(b, off, len);
172                if (msg.equals(endOfLineSystem) || msg.equals(endOfLineShort)) { // this is a newline sequence only
173                        ejectBuffer();
174                }
175                else {
176                        byteStream.write(b, off, len);  // append message to buffer
177                        if (msg.endsWith(endOfLineSystem) || msg.endsWith(endOfLineShort)) { // line terminated by Newline
178                                // note that this does not seem to happen ever (even with format)!?
179                                ejectBuffer();
180                        }
181                }
182        }
183        
184        @Override
185        // ever called?
186        public void write(int b) {
187                byteStream.write(b);
188        }
189
190        @Override
191        public void flush() {
192                if (byteStream.size() > 0) {
193                        ejectBuffer();
194                }
195                super.flush();
196        }
197        
198        @Override
199        public void close() {
200                super.close();
201        }
202        
203        private void ejectBuffer() {
204                IJ.log(prefix + byteStream.toString());
205                byteStream.reset();
206        }
207        
208}