root/trunk/src/java/org/jcoderz/commons/util/LazyFileOutputStream.java

Revision 1068, 6.6 kB (checked in by amandel, 4 years ago)

Added log output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.commons.util;
34
35import java.io.BufferedInputStream;
36import java.io.ByteArrayOutputStream;
37import java.io.File;
38import java.io.FileInputStream;
39import java.io.FileNotFoundException;
40import java.io.FileOutputStream;
41import java.io.IOException;
42import java.io.InputStream;
43import java.io.OutputStream;
44import java.util.logging.Logger;
45
46/**
47 * Purpose of this class is to avoid writing a File if it already
48 * exists and hold the same content.
49 *
50 * <p>This class in not about saving time for faster processing nor
51 * does is perform anything quicker. It sole checks if the target
52 * file exists, and if its content is already the same than
53 * the content that should be written. If this is the case no
54 * write operation is done at all. Therefore also the file content
55 * to be written is held in memory until the file is closed OR
56 * it is detected that the content did change.</p>
57 *
58 * <p>Instances of this class are NOT multi thread save.</p>
59 *
60 * @author Andreas Mandel
61 */
62public class LazyFileOutputStream
63    extends OutputStream
64{
65    private static final String CLASSNAME
66        = LazyFileOutputStream.class.getName();
67    private static final Logger logger = Logger.getLogger(CLASSNAME);
68    private boolean mBuffering;
69    private OutputStream mOutput;
70    private File mFile;
71    private InputStream mInputStream;
72
73
74    /** @see FileOutputStream#FileOutputStream(String, boolean) */
75    public LazyFileOutputStream (File file, boolean append)
76        throws FileNotFoundException
77    {
78        mFile = file;
79        if (append)
80        {
81            mBuffering = false;
82            mOutput = new FileOutputStream(file, append);
83        }
84        else
85        {
86            if (file.exists() && file.canRead())
87            {   // Should be OK to let a possible exception fall through
88                mInputStream =
89                    new BufferedInputStream(new FileInputStream(file));
90                mOutput = new ByteArrayOutputStream();
91                mBuffering = true;
92            }
93            else
94            {
95                mOutput = new FileOutputStream(file, append);
96                mBuffering = false;
97            }
98        }
99    }
100
101    /** @see FileOutputStream#FileOutputStream(File) */
102    public LazyFileOutputStream (File file)
103        throws FileNotFoundException
104    {
105        this(file, false);
106    }
107
108    /** @see FileOutputStream#FileOutputStream(String, boolean) */
109    public LazyFileOutputStream (String name, boolean append)
110        throws FileNotFoundException
111    {
112        this(new File(name), append);
113    }
114
115    /** @see FileOutputStream#FileOutputStream(String) */
116    public LazyFileOutputStream (String name)
117        throws FileNotFoundException
118    {
119        this(new File(name));
120    }
121
122    /**
123     * Returns true if the file is still buffered.
124     * The value might change from true to false until
125     * the stream is closed. After this the returned value will not
126     * change any more.
127     * @return true if the actual out file was not touched (yet).
128     */
129    public boolean isBuffered ()
130    {
131        return mBuffering;
132    }
133
134    /** {@inheritDoc} */
135    public void write (int b)
136        throws IOException
137    {
138        ensureOpen();
139        if (mBuffering &&  mInputStream.read() != b)
140        {
141            stopBuffering();
142        }
143        mOutput.write(b);
144    }
145
146    // TODO: For better performance implement the other write methods!
147
148    /** {@inheritDoc} */
149    public void close ()
150        throws IOException
151    {
152        if (mBuffering && mInputStream.read() != -1)
153        {   // new output is shorter...
154            stopBuffering();
155        }
156        IoUtil.close(mInputStream);
157        if (mOutput != null)
158        {
159            mOutput.close();
160            mOutput = null;
161            if (mBuffering)
162            {
163                logger.fine("Avoided to touch " + mFile);
164            }
165        }
166    }
167
168    /**
169     * Cleans up the connection to the file, and ensures that the
170     * <code>close</code> method of this file output stream is
171     * called when there are no more references to this stream.
172     *
173     * @exception  IOException  if an I/O error occurs.
174     * @see        java.io.FileInputStream#close()
175     */
176    protected void finalize ()
177        throws IOException, Throwable
178    {
179        if (mOutput != null)
180        {
181            close();
182        }
183        super.finalize();
184    }
185
186
187    /**
188     * Check to make sure that output stream has not been nulled
189     * out due to close
190     */
191    private void ensureOpen ()
192        throws IOException
193    {
194        if (mOutput == null)
195        {
196            throw new IOException("Stream closed");
197        }
198    }
199
200    private void stopBuffering ()
201        throws IOException
202    {
203        IoUtil.close(mInputStream);
204        IoUtil.close(mOutput);
205        // sanity
206        if (!(mOutput instanceof ByteArrayOutputStream))
207        {
208            throw new RuntimeException("Internal inconsistency");
209        }
210        final byte[] data
211            = ((ByteArrayOutputStream) mOutput).toByteArray();
212        mOutput = new FileOutputStream(mFile);
213        mOutput.write(data);
214        mBuffering = false;
215    }
216}
Note: See TracBrowser for help on using the browser.