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.stream.FilterOutputStream;
13 
14 import hunt.stream.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  */
36 class FilterOutputStream : OutputStream {
37     /**
38      * The underlying output stream to be filtered.
39      */
40     protected OutputStream outputStream;
41 
42     /**
43      * Whether the stream is closed; implicitly initialized to false.
44      */
45     private bool closed;
46 
47     /**
48      * Object used to prevent a race on the 'closed' instance variable.
49      */
50     private Object closeLock;
51 
52     /**
53      * Creates an output stream filter built on top of the specified
54      * underlying output stream.
55      *
56      * @param   outputStream   the underlying output stream to be assigned to
57      *                the field {@code this.outputStream} for later use, or
58      *                <code>null</code> if this instance is to be
59      *                created without an underlying stream.
60      */
61     this(OutputStream outputStream) {
62         this.outputStream = outputStream;
63         closeLock = new Object();
64     }
65 
66     /**
67      * Writes the specified <code>byte</code> to this output stream.
68      * <p>
69      * The <code>write</code> method of <code>FilterOutputStream</code>
70      * calls the <code>write</code> method of its underlying output stream,
71      * that is, it performs {@code outputStream.write(b)}.
72      * <p>
73      * Implements the abstract {@code write} method of {@code OutputStream}.
74      *
75      * @param      b   the <code>byte</code>.
76      * @exception  IOException  if an I/O error occurs.
77      */
78     override
79     void write(int b) {
80         outputStream.write(b);
81     }
82 
83     /**
84      * Writes <code>b.length</code> bytes to this output stream.
85      * <p>
86      * The <code>write</code> method of <code>FilterOutputStream</code>
87      * calls its <code>write</code> method of three arguments with the
88      * arguments <code>b</code>, <code>0</code>, and
89      * <code>b.length</code>.
90      * <p>
91      * Note that this method does not call the one-argument
92      * <code>write</code> method of its underlying output stream with
93      * the single argument <code>b</code>.
94      *
95      * @param      b   the data to be written.
96      * @exception  IOException  if an I/O error occurs.
97      * @see        java.io.FilterOutputStream#write(byte[], int, int)
98      */
99     override
100     void write(byte[] b) {
101         write(b, 0, cast(int)b.length);
102     }
103 
104     /**
105      * Writes <code>len</code> bytes from the specified
106      * <code>byte</code> array starting at offset <code>off</code> to
107      * this output stream.
108      * <p>
109      * The <code>write</code> method of <code>FilterOutputStream</code>
110      * calls the <code>write</code> method of one argument on each
111      * <code>byte</code> to output.
112      * <p>
113      * Note that this method does not call the <code>write</code> method
114      * of its underlying output stream with the same arguments. Subclasses
115      * of <code>FilterOutputStream</code> should provide a more efficient
116      * implementation of this method.
117      *
118      * @param      b     the data.
119      * @param      off   the start offset in the data.
120      * @param      len   the number of bytes to write.
121      * @exception  IOException  if an I/O error occurs.
122      * @see        java.io.FilterOutputStream#write(int)
123      */
124     override
125     void write(byte[] b, int off, int len) {
126         if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
127             throw new IndexOutOfBoundsException();
128 
129         for (int i = 0 ; i < len ; i++) {
130             write(b[off + i]);
131         }
132     }
133 
134     /**
135      * Flushes this output stream and forces any buffered output bytes
136      * to be written out to the stream.
137      * <p>
138      * The <code>flush</code> method of <code>FilterOutputStream</code>
139      * calls the <code>flush</code> method of its underlying output stream.
140      *
141      * @exception  IOException  if an I/O error occurs.
142      * @see        java.io.FilterOutputStream#outputStream
143      */
144     override
145     void flush() {
146         outputStream.flush();
147     }
148 
149     /**
150      * Closes this output stream and releases any system resources
151      * associated with the stream.
152      * <p>
153      * When not already closed, the {@code close} method of {@code
154      * FilterOutputStream} calls its {@code flush} method, and then
155      * calls the {@code close} method of its underlying output stream.
156      *
157      * @exception  IOException  if an I/O error occurs.
158      * @see        java.io.FilterOutputStream#flush()
159      * @see        java.io.FilterOutputStream#outputStream
160      */
161     override
162     void close() {
163         if (closed) {
164             return;
165         }
166         synchronized (closeLock) {
167             if (closed) {
168                 return;
169             }
170             closed = true;
171         }
172 
173         Throwable flushException = null;
174         try {
175             flush();
176         } catch (Throwable e) {
177             flushException = e;
178             throw e;
179         } finally {
180             outputStream.close();            
181         }
182     }
183 }