SquirrelJME/SquirrelJME

View on GitHub
modules/io/src/main/java/net/multiphasicapps/io/ExtendedDataOutputStream.java

Summary

Maintainability
A
0 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package net.multiphasicapps.io;

import cc.squirreljme.runtime.cldc.debug.Debugging;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import static cc.squirreljme.runtime.cldc.debug.ErrorCode.__error__;

/**
 * This is an extended output stream which is better suited to writing
 * general binaries compared to the standard {@link DataOutputStream}.
 *
 * Streams default to big endian.
 *
 * This class is not thread safe.
 *
 * {@squirreljme.error BD0w Unhandled endianess. (The endianess)}
 *
 * @since 2016/07/10
 */
public class ExtendedDataOutputStream
    extends OutputStream
    implements DataOutput, SettableEndianess, SizedStream
{
    /** The output data stream. */
    protected final DataOutputStream output;
    
    /** The target endianess. */
    private volatile DataEndianess _endian =
        DataEndianess.BIG;
    
    /** The current file size. */
    private volatile long _size;
    
    /**
     * Initializes the extended data output stream.
     *
     * @param __os The stream to write data to.
     * @throws NullPointerException On null arguments.
     * @since 2016/07/10
     */
    public ExtendedDataOutputStream(OutputStream __os)
        throws NullPointerException
    {
        // Check
        if (__os == null)
            throw new NullPointerException("NARG");
        
        // Set
        this.output = ((__os instanceof DataOutputStream) ?
            (DataOutputStream)__os : new DataOutputStream(__os));
    }
    
    /**
     * Aligns the output to the given number of bytes.
     *
     * @param __n The number of bytes to align to.
     * @throws IndexOutOfBoundsException If the alignment amount is zero or
     * negative.
     * @throws IOException On write errors.
     * @since 2016/09/11
     */
    public void align(int __n)
        throws IndexOutOfBoundsException, IOException
    {
        /* {@squirreljme.error BD0x Cannot align to zero or a negative
        amount.} */
        if (__n <= 0)
            throw new IndexOutOfBoundsException(__error__(1234));
        
        // Pad
        while ((this.size() % __n) != 0)
            this.write(0);
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public void close()
        throws IOException
    {
        this.output.close();
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public void flush()
        throws IOException
    {
        this.output.flush();
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final DataEndianess getEndianess()
    {
        return this._endian;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final DataEndianess setEndianess(DataEndianess __end)
        throws NullPointerException
    {
        // Check
        if (__end == null)
            throw new NullPointerException("NARG");
        
        // Get and set
        DataEndianess rv = this._endian;
        this._endian = __end;
        return rv;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final long size()
    {
        return this._size;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void write(byte[] __b)
        throws IOException, NullPointerException
    {
        // Check
        if (__b == null)
            throw new NullPointerException("NARG");
        
        // Forward
        this.write(__b, 0, __b.length);
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void write(byte[] __b, int __o, int __l)
        throws IndexOutOfBoundsException, IOException, NullPointerException
    {
        // Check
        if (__b == null)
            throw new NullPointerException("NARG");
        int n = __b.length;
        if (__o < 0 || __l < 0 || (__o + __l) > n)
            throw new IndexOutOfBoundsException("IOOB");
        
        // Write
        this.output.write(__b, __o, __l);
        
        // Add size
        this._size += __l;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void write(int __b)
        throws IOException
    {
        this.output.write(__b);
        this._size++;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeBoolean(boolean __v)
        throws IOException
    {
        this.writeByte((__v ? 1 : 0));
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeByte(int __v)
        throws IOException
    {
        this.output.write(__v);
        this._size++;
    }
    
    /**
     * Writes a single byte, if its value is out of range then a write error
     * occurs.
     *
     * @param __v The byte to write.
     * @throws IOException On out of range or other write errors.
     * @since 2016/09/14
     */
    public final void writeByteExact(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD0y Byte value out of range.} */
        if (__v < -128 || __v > 127)
            throw new IOException("BD0y");
        
        this.writeByte(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeBytes(String __s)
        throws IOException
    {
        throw Debugging.todo();
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeChar(int __v)
        throws IOException
    {
        this.writeShort(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeChars(String __s)
        throws IOException, NullPointerException
    {
        // Check
        if (__s == null)
            throw new NullPointerException("NARG");
        
        // Write all characters
        int n = __s.length();
        for (int i = 0; i < n; i++)
            this.writeShort(__s.charAt(i));
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeDouble(double __v)
        throws IOException
    {
        this.writeLong(Double.doubleToRawLongBits(__v));
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeFloat(float __v)
        throws IOException
    {
        this.writeInt(Float.floatToRawIntBits(__v));
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeInt(int __v)
        throws IOException
    {
        // Depends on the endian
        DataOutputStream output = this.output;
        DataEndianess endian = this._endian;
        switch (endian)
        {
                // Big
            case BIG:
                output.writeInt(__v);
                break;
                
                // Little
            case LITTLE:
                output.writeInt(Integer.reverseBytes(__v));
                break;
            
                // Unknown
            default:
                throw new IOException(String.format("BD04 %s", endian));
        }
        
        // Increase
        this._size += 4;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeLong(long __v)
        throws IOException
    {
        // Depends on the endianess
        DataEndianess endian = this._endian;
        DataOutputStream output = this.output;
        switch (endian)
        {
                // Big
            case BIG:
                output.writeLong(__v);
                break;
                
                // Little
            case LITTLE:
                output.writeLong(Long.reverseBytes(__v));
                break;
            
                // Unknown
            default:
                throw new IOException(String.format("BD04 %s", endian));
        }
        
        // Increase
        this._size += 8;
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeShort(int __v)
        throws IOException
    {
        // Depends on the endian
        DataEndianess endian = this._endian;
        DataOutputStream output = this.output;
        switch (endian)
        {
                // Big
            case BIG:
                output.writeShort(__v);
                break;
                
                // Little
            case LITTLE:
                output.writeShort(Short.reverseBytes((short)__v));
                break;
            
                // Unknown
            default:
                throw new IOException(String.format("BD04 %s", endian));
        }
        
        // Increase
        this._size += 2;
    }
    
    /**
     * Writes a single short, if its value is out of range then a write error
     * occurs.
     *
     * @param __v The short to write.
     * @throws IOException On out of range or other write errors.
     * @since 2016/09/14
     */
    public final void writeShortExact(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD0z Short value out of range.} */
        if (__v < -32768 || __v > 32767)
            throw new IOException("BD0z");
        
        this.writeShort(__v);
    }
    
    /**
     * Writes a single unsigned byte, if its value is out of range then a
     * write error occurs.
     *
     * @param __v The unsigned byte to write.
     * @throws IOException On out of range or other write errors.
     * @since 2016/09/14
     */
    public final void writeUnsignedByteExact(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD10 Unsigned byte value out of range.} */
        if (__v < 0 || __v > 255)
            throw new IOException("BD10");
        
        this.writeByte(__v);
    }
    
    /**
     * Writes a single unsigned byte, if its value is out of range then a
     * write error occurs.
     *
     * @param __v The unsigned short to write.
     * @throws IOException On out of range or other write errors.
     * @since 2016/09/14
     */
    public final void writeUnsignedShortExact(int __v)
        throws IOException
    {
        /* {@squirreljme.error BD11 Unsigned short value out of range.} */
        if (__v < 0 || __v > 65535)
            throw new IOException("BD11");
        
        this.writeShort(__v);
    }
    
    /**
     * {@inheritDoc}
     * @since 2016/07/10
     */
    @Override
    public final void writeUTF(String __s)
        throws IOException
    {
        throw Debugging.todo();
    }
}