/* * Copyright (C) 2011. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 or * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package uk.me.parabola.imgfmt.app; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import uk.me.parabola.imgfmt.MapFailedException; import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.fs.ImgChannel; /** * Write img file data to a temporary file. On a call to sync() the data * is copied to the output channel. * * @author Steve Ratcliffe */ public class FileBackedImgFileWriter implements ImgFileWriter{ private final ImgChannel outputChan; private final File tmpFile; private final BufferedOutputStream file; private final FileChannel tmpChannel; public FileBackedImgFileWriter(ImgChannel chan, File outputDir) { this.outputChan = chan; try { tmpFile = File.createTempFile("img", null, outputDir); tmpFile.deleteOnExit(); FileOutputStream out = new FileOutputStream(tmpFile); tmpChannel = out.getChannel(); file = new BufferedOutputStream(out, 16*1024); } catch (IOException e) { throw new MapFailedException("Could not create mdr temporary file"); } } /** * Maps the temporary file and copies to the output channel. * * @throws IOException If there is an error writing. */ public void sync() throws IOException { file.close(); FileInputStream is = null; try { is = new FileInputStream(tmpFile); FileChannel channel = is.getChannel(); channel.transferTo(0, channel.size(), outputChan); channel.close(); } finally { Utils.closeFile(is); if (!tmpFile.delete()) System.err.println("Could not delete mdr img temporary file"); } } /** * Get the position. Have to flush the buffer before getting the position. * * @return The logical position within the file. */ public int position() { try { file.flush(); return (int) tmpChannel.position(); } catch (IOException e) { return 0; } } /** * Set the position of the file. * The buffer has to be flushed first. * * @param pos The new position in the file. */ public void position(long pos) { try { file.flush(); tmpChannel.position(pos); } catch (IOException e) { throw new MapFailedException("Could not set position in mdr tmp file"); } } /** * Write out a single byte. * * @param b The byte to write. */ public void put(byte b) { try { file.write(b); } catch (IOException e) { throw new MapFailedException("could not write byte to mdr tmp file"); } } /** * Write out two bytes. Can't use writeChar() since need to reverse the byte * order. * * @param c The value to write. */ public void putChar(char c) { try { file.write(c); file.write(c >> 8); } catch (IOException e) { throw new MapFailedException("could not write char to mdr tmp file"); } } /** * Write out three bytes. Done in the little endian byte order. * * @param val The value to write, only the bottom three bytes will be written. */ public void put3(int val) { try { file.write(val); file.write(val >> 8); file.write(val >> 16); } catch (IOException e) { throw new MapFailedException("could not write3 to mdr tmp file"); } } /** * Write out 4 byte value. * * @param val The value to write. */ public void putInt(int val) { try { file.write(val); file.write(val >> 8); file.write(val >> 16); file.write(val >> 24); } catch (IOException e) { throw new MapFailedException("could not write int to mdr tmp file"); } } /** * Write out an arbitrary length sequence of bytes. * * @param val The values to write. */ public void put(byte[] val) { try { file.write(val); } catch (IOException e) { throw new MapFailedException("could not write bytes to mdr tmp file"); } } /** * Write out part of a byte array. * * @param src The array to take bytes from. * @param start The start position. * @param length The number of bytes to write. */ public void put(byte[] src, int start, int length) { try { file.write(src, start, length); } catch (IOException e) { throw new MapFailedException("could not write bytes to mdr tmp file"); } } /** * Write out a complete byte buffer. * * @param src The buffer to write. */ public void put(ByteBuffer src) { try { file.flush(); tmpChannel.write(src); } catch (IOException e) { throw new MapFailedException("could not write buffer to mdr tmp file"); } } /** * Returns the size of the file. * * @return The file size in bytes. */ public long getSize() { try { file.flush(); return tmpChannel.size(); } catch (IOException e) { throw new MapFailedException("could not get size of mdr tmp file"); } } /** * Closes this stream and releases any system resources associated with it. If the stream is already closed then * invoking this method has no effect. * * @throws IOException if an I/O error occurs */ public void close() throws IOException { outputChan.close(); } }