/*
 * SQLCopyTools.java
 * 
 * (C) 24.06.2004 Markus Schaber, Logi-Track ag, CH 8001 Zuerich
 * 
 * This file is intended to be included into the official postgresql jdbc
 * project and implicitly relicensed under their Berkeley/BSD licence when
 * submitted to them by a Logi-Track developer.
 * 
 * $Id: SQLCopyWriteHelper.java,v 1.1 2004/06/24 16:39:34 schabi Exp $
 */
package com.logitrack.gis.util;

import java.io.IOException;
import java.io.OutputStream;

/**
 * SQLCopyTools - Utility class that helps to create an appropriate byte stream 
 * for the (currently experimental) SQL Copy command.
 * 
 * @author schabi
 *  
 */
public class SQLCopyWriteHelper {

    public static byte[] TERMINATOR = new byte[]{'\\', '.', '\n'};
    public static byte[] NULLVALUE = new byte[]{'\\', 'N'};
    public static byte NEWROW = '\n';
    public static byte NEWCOL = '\t';

    public static byte[] BACK = new byte[]{'\\', 'b'};
    public static byte[] FF = new byte[]{'\\', 'f'};
    public static byte[] NL = new byte[]{'\\', 'n'};
    public static byte[] CR = new byte[]{'\\', 'r'};
    public static byte[] TAB = new byte[]{'\\', 't'};
    public static byte[] VTAB = new byte[]{'\\', 'v'};
    public static byte[] ASC0 = new byte[]{'\\', '0', '0', '0'};

    private OutputStream outstream;

    public SQLCopyWriteHelper(OutputStream baos) {
        outstream = baos;
    }

    public void endStream() throws IOException {
        outstream.write(TERMINATOR);
    }

    public void nextRow() throws IOException {
        outstream.write(NEWROW);
    }

    public void nextColumn() throws IOException {
        outstream.write(NEWCOL);
    }

    public void addNull() throws IOException {
        outstream.write(NULLVALUE);
    }

    public void escapeAndAdd(byte data) throws IOException {
        switch (data) {
        case 0 :
            throw new IOException("NULL bytes not allowed!");
        case 8 :
            outstream.write(BACK);
            break;
        case 9 :
            outstream.write(TAB);
            break;
        case 10 :
            outstream.write(NL);
            break;
        case 11 :
            outstream.write(VTAB);
            break;
        case 12 :
            outstream.write(FF);
            break;
        case 13 :
            outstream.write(CR);
            break;
        default :
            outstream.write(data);
            break;
        }
    }

    public void add(byte[] data) throws IOException {
        if (data == null) {
            addNull();
        } else {
            for (int i = 0; i < data.length; i++) {
                escapeAndAdd(data[i]);
            }
        }
    }

    public void add(Object Obj) throws IOException {
        String text = Obj.toString();
        if (text == null) {
            addNull();
        } else {
            byte[] data = text.getBytes("UTF-8");
            for (int i = 0; i < data.length; i++) {
                escapeAndAdd(data[i]);
            }
        }
    }

    /**
     * @param bv
     * @throws IOException
     */
    public void add(boolean bv) throws IOException {
        if (bv) {
            outstream.write('t');
        } else {
            outstream.write('f');
        }
    }

    public void add(double d) throws IOException {
        add(Double.toString(d));
    }

    public void add(long l) throws IOException {
        if (l==0l) {
            outstream.write('0');
        } else {
            addrecurse(l);
        }
    }
    
    private void addrecurse(long l) throws IOException {
        if (l>10) {
            addrecurse(l/10);
        }
        outstream.write((int)('0'+(l%10)));
    }
}