1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package org.apache.commons.io.output;
18
19import java.io.IOException;
20import java.io.OutputStream;
21
22
23/**
24 * An output stream which triggers an event when a specified number of bytes of
25 * data have been written to it. The event can be used, for example, to throw
26 * an exception if a maximum has been reached, or to switch the underlying
27 * stream type when the threshold is exceeded.
28 * <p>
29 * This class overrides all <code>OutputStream</code> methods. However, these
30 * overrides ultimately call the corresponding methods in the underlying output
31 * stream implementation.
32 * <p>
33 * NOTE: This implementation may trigger the event <em>before</em> the threshold
34 * is actually reached, since it triggers when a pending write operation would
35 * cause the threshold to be exceeded.
36 *
37 * @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
38 *
39 * @version $Id: ThresholdingOutputStream.java 540714 2007-05-22 19:39:44Z niallp $
40 */
41public abstract class ThresholdingOutputStream
42    extends OutputStream
43{
44
45    // ----------------------------------------------------------- Data members
46
47
48    /**
49     * The threshold at which the event will be triggered.
50     */
51    private int threshold;
52
53
54    /**
55     * The number of bytes written to the output stream.
56     */
57    private long written;
58
59
60    /**
61     * Whether or not the configured threshold has been exceeded.
62     */
63    private boolean thresholdExceeded;
64
65
66    // ----------------------------------------------------------- Constructors
67
68
69    /**
70     * Constructs an instance of this class which will trigger an event at the
71     * specified threshold.
72     *
73     * @param threshold The number of bytes at which to trigger an event.
74     */
75    public ThresholdingOutputStream(int threshold)
76    {
77        this.threshold = threshold;
78    }
79
80
81    // --------------------------------------------------- OutputStream methods
82
83
84    /**
85     * Writes the specified byte to this output stream.
86     *
87     * @param b The byte to be written.
88     *
89     * @exception IOException if an error occurs.
90     */
91    public void write(int b) throws IOException
92    {
93        checkThreshold(1);
94        getStream().write(b);
95        written++;
96    }
97
98
99    /**
100     * Writes <code>b.length</code> bytes from the specified byte array to this
101     * output stream.
102     *
103     * @param b The array of bytes to be written.
104     *
105     * @exception IOException if an error occurs.
106     */
107    public void write(byte b[]) throws IOException
108    {
109        checkThreshold(b.length);
110        getStream().write(b);
111        written += b.length;
112    }
113
114
115    /**
116     * Writes <code>len</code> bytes from the specified byte array starting at
117     * offset <code>off</code> to this output stream.
118     *
119     * @param b   The byte array from which the data will be written.
120     * @param off The start offset in the byte array.
121     * @param len The number of bytes to write.
122     *
123     * @exception IOException if an error occurs.
124     */
125    public void write(byte b[], int off, int len) throws IOException
126    {
127        checkThreshold(len);
128        getStream().write(b, off, len);
129        written += len;
130    }
131
132
133    /**
134     * Flushes this output stream and forces any buffered output bytes to be
135     * written out.
136     *
137     * @exception IOException if an error occurs.
138     */
139    public void flush() throws IOException
140    {
141        getStream().flush();
142    }
143
144
145    /**
146     * Closes this output stream and releases any system resources associated
147     * with this stream.
148     *
149     * @exception IOException if an error occurs.
150     */
151    public void close() throws IOException
152    {
153        try
154        {
155            flush();
156        }
157        catch (IOException ignored)
158        {
159            // ignore
160        }
161        getStream().close();
162    }
163
164
165    // --------------------------------------------------------- Public methods
166
167
168    /**
169     * Returns the threshold, in bytes, at which an event will be triggered.
170     *
171     * @return The threshold point, in bytes.
172     */
173    public int getThreshold()
174    {
175        return threshold;
176    }
177
178
179    /**
180     * Returns the number of bytes that have been written to this output stream.
181     *
182     * @return The number of bytes written.
183     */
184    public long getByteCount()
185    {
186        return written;
187    }
188
189
190    /**
191     * Determines whether or not the configured threshold has been exceeded for
192     * this output stream.
193     *
194     * @return <code>true</code> if the threshold has been reached;
195     *         <code>false</code> otherwise.
196     */
197    public boolean isThresholdExceeded()
198    {
199        return (written > threshold);
200    }
201
202
203    // ------------------------------------------------------ Protected methods
204
205
206    /**
207     * Checks to see if writing the specified number of bytes would cause the
208     * configured threshold to be exceeded. If so, triggers an event to allow
209     * a concrete implementation to take action on this.
210     *
211     * @param count The number of bytes about to be written to the underlying
212     *              output stream.
213     *
214     * @exception IOException if an error occurs.
215     */
216    protected void checkThreshold(int count) throws IOException
217    {
218        if (!thresholdExceeded && (written + count > threshold))
219        {
220            thresholdExceeded = true;
221            thresholdReached();
222        }
223    }
224
225    /**
226     * Resets the byteCount to zero.  You can call this from
227     * {@link #thresholdReached()} if you want the event to be triggered again.
228     */
229    protected void resetByteCount()
230    {
231        this.thresholdExceeded = false;
232        this.written = 0;
233    }
234
235    // ------------------------------------------------------- Abstract methods
236
237
238    /**
239     * Returns the underlying output stream, to which the corresponding
240     * <code>OutputStream</code> methods in this class will ultimately delegate.
241     *
242     * @return The underlying output stream.
243     *
244     * @exception IOException if an error occurs.
245     */
246    protected abstract OutputStream getStream() throws IOException;
247
248
249    /**
250     * Indicates that the configured threshold has been reached, and that a
251     * subclass should take whatever action necessary on this event. This may
252     * include changing the underlying output stream.
253     *
254     * @exception IOException if an error occurs.
255     */
256    protected abstract void thresholdReached() throws IOException;
257}
258