1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.io.FilterOutputStream;
13 
14 import hunt.io.Common;
15 import hunt.Exceptions;
16 
17 version(HUNT_DEBUG) {
18     import hunt.logging;
19 }
20 
21 /**
22  * This class is the superclass of all classes that filter output
23  * streams. These streams sit on top of an already existing output
24  * stream (the <i>underlying</i> output stream) which it uses as its
25  * basic sink of data, but possibly transforming the data along the
26  * way or providing additional functionality.
27  * <p>
28  * The class <code>FilterOutputStream</code> itself simply overrides
29  * all methods of <code>OutputStream</code> with versions that pass
30  * all requests to the underlying output stream. Subclasses of
31  * <code>FilterOutputStream</code> may further override some of these
32  * methods as well as provide additional methods and fields.
33  *
34  * @author  Jonathan Payne
35  * @since   1.0
36  */
37 class FilterOutputStream : OutputStream {
38     /**
39      * The underlying output stream to be filtered.
40      */
41     protected OutputStream outputStream;
42 
43     /**
44      * Whether the stream is closed; implicitly initialized to false.
45      */
46     private bool closed;
47 
48     /**
49      * Object used to prevent a race on the 'closed' instance variable.
50      */
51     private Object closeLock;
52 
53     /**
54      * Creates an output stream filter built on top of the specified
55      * underlying output stream.
56      *
57      * @param   outputStream   the underlying output stream to be assigned to
58      *                the field {@code this.outputStream} for later use, or
59      *                <code>null</code> if this instance is to be
60      *                created without an underlying stream.
61      */
62     this(OutputStream outputStream) {
63         this.outputStream = outputStream;
64         closeLock = new Object();
65     }
66 
67     /**
68      * Writes the specified <code>byte</code> to this output stream.
69      * <p>
70      * The <code>write</code> method of <code>FilterOutputStream</code>
71      * calls the <code>write</code> method of its underlying output stream,
72      * that is, it performs {@code outputStream.write(b)}.
73      * <p>
74      * Implements the abstract {@code write} method of {@code OutputStream}.
75      *
76      * @param      b   the <code>byte</code>.
77      * @exception  IOException  if an I/O error occurs.
78      */
79     override
80     void write(int b) {
81         outputStream.write(b);
82     }
83 
84     /**
85      * Writes <code>b.length</code> bytes to this output stream.
86      * <p>
87      * The <code>write</code> method of <code>FilterOutputStream</code>
88      * calls its <code>write</code> method of three arguments with the
89      * arguments <code>b</code>, <code>0</code>, and
90      * <code>b.length</code>.
91      * <p>
92      * Note that this method does not call the one-argument
93      * <code>write</code> method of its underlying output stream with
94      * the single argument <code>b</code>.
95      *
96      * @param      b   the data to be written.
97      * @exception  IOException  if an I/O error occurs.
98      * @see        java.io.FilterOutputStream#write(byte[], int, int)
99      */
100     override
101     void write(byte[] b) {
102         write(b, 0, cast(int)b.length);
103     }
104 
105     /**
106      * Writes <code>len</code> bytes from the specified
107      * <code>byte</code> array starting at offset <code>off</code> to
108      * this output stream.
109      * <p>
110      * The <code>write</code> method of <code>FilterOutputStream</code>
111      * calls the <code>write</code> method of one argument on each
112      * <code>byte</code> to output.
113      * <p>
114      * Note that this method does not call the <code>write</code> method
115      * of its underlying output stream with the same arguments. Subclasses
116      * of <code>FilterOutputStream</code> should provide a more efficient
117      * implementation of this method.
118      *
119      * @param      b     the data.
120      * @param      off   the start offset in the data.
121      * @param      len   the number of bytes to write.
122      * @exception  IOException  if an I/O error occurs.
123      * @see        java.io.FilterOutputStream#write(int)
124      */
125     override
126     void write(byte[] b, int off, int len) {
127         if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
128             throw new IndexOutOfBoundsException();
129 
130         for (int i = 0 ; i < len ; i++) {
131             write(b[off + i]);
132         }
133     }
134 
135     /**
136      * Flushes this output stream and forces any buffered output bytes
137      * to be written out to the stream.
138      * <p>
139      * The <code>flush</code> method of <code>FilterOutputStream</code>
140      * calls the <code>flush</code> method of its underlying output stream.
141      *
142      * @exception  IOException  if an I/O error occurs.
143      * @see        java.io.FilterOutputStream#outputStream
144      */
145     override
146     void flush() {
147         outputStream.flush();
148     }
149 
150     /**
151      * Closes this output stream and releases any system resources
152      * associated with the stream.
153      * <p>
154      * When not already closed, the {@code close} method of {@code
155      * FilterOutputStream} calls its {@code flush} method, and then
156      * calls the {@code close} method of its underlying output stream.
157      *
158      * @exception  IOException  if an I/O error occurs.
159      * @see        java.io.FilterOutputStream#flush()
160      * @see        java.io.FilterOutputStream#outputStream
161      */
162     override
163     void close() {
164         if (closed) {
165             return;
166         }
167         synchronized (closeLock) {
168             if (closed) {
169                 return;
170             }
171             closed = true;
172         }
173 
174         Throwable flushException = null;
175         try {
176             flush();
177         } catch (Throwable e) {
178             flushException = e;
179             throw e;
180         } finally {
181             outputStream.close();            
182         }
183     }
184 }