Subversion Repositories mkgmap

Rev

Rev 2046 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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();
        }
}