package org.maachang.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * GZIPバイナリ変換.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangBase 1.00
 */
public class GZIPBinary {
    
    /**
     * バッファ長.
     */
    private static final int BUF_LENGTH = 1024 ;
    
    /**
     * シングルトン.
     */
    private static final GZIPBinary SNGL = new GZIPBinary() ;
    
    /**
     * コンストラクタ.
     */
    private GZIPBinary() {
    }
    
    /**
     * オブジェクトを取得.
     * <BR><BR>
     * オブジェクトを取得します.
     * <BR>
     * @return GZIPBinary オブジェクト情報が返されます.
     */
    public static final GZIPBinary getInstance() {
        return SNGL ;
    }
    
    /**
     * 指定されたバイナリ情報を圧縮.
     * <BR><BR>
     * 指定されたバイナリ情報を圧縮します.
     * <BR>
     * @param binary 圧縮対象のバイナリ情報を設定します.
     * @return byte[] 圧縮されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertBinaryByGZIP( byte[] binary )
        throws IllegalArgumentException,IOException {
        if( binary == null ){
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        return this.convertBinaryByGZIP( binary,0,binary.length ) ;
    }
    
    /**
     * 指定されたバイナリ情報を圧縮.
     * <BR><BR>
     * 指定されたバイナリ情報を圧縮します.
     * <BR>
     * @param binary 圧縮対象のバイナリ情報を設定します.
     * @param length 圧縮情報長を設定します.
     * @return byte[] 圧縮されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertBinaryByGZIP( byte[] binary,int length )
        throws IllegalArgumentException,IOException
    {
        if( binary == null ){
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        return this.convertBinaryByGZIP( binary,0,length ) ;
    }
    
    /**
     * 指定されたバイナリ情報を圧縮.
     * <BR><BR>
     * 指定されたバイナリ情報を圧縮します.
     * <BR>
     * @param binary 圧縮対象のバイナリ情報を設定します.
     * @param offset 圧縮情報開始位置を設定します.
     * @param length 圧縮情報長を設定します.
     * @return byte[] 圧縮されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertBinaryByGZIP( byte[] binary,int offset,int length )
        throws IllegalArgumentException,IOException
    {
        int len ;
        
        ByteArrayOutputStream bin = null ;
        GZIPOutputStream zip = null ;
        BufferedOutputStream buf = null ;
        
        byte[] ret = null ;
        
        if(
            binary == null || ( len = binary.length ) <= 0 ||
            offset < 0 || offset > len ||
            length <= 0 || length > len ||
            ( offset + length ) > len
            
        )
        {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        try{
            
            bin = new ByteArrayOutputStream() ;
            zip = new GZIPOutputStream( bin ) ;
            buf = new BufferedOutputStream( zip ) ;
            
            buf.write( binary,offset,length ) ;
            buf.flush() ;
            zip.finish() ;
            
            ret = bin.toByteArray() ;
            
        }catch( IOException io ){
            throw io ;
        }finally{
            
            try{
                bin.close() ;
            }catch( Exception t ){
            }
            
            try{
                zip.close() ;
            }catch( Exception t ){
            }
            
            try{
                buf.close() ;
            }catch( Exception t ){
            }
            
            bin = null ;
            buf = null ;
            zip = null ;
            
        }
        
        return ret ;
        
    }
    
    /**
     * 指定された圧縮バイナリ情報を解凍.
     * <BR><BR>
     * 指定された圧縮バイナリ情報を解凍します.
     * <BR>
     * @param zbin 解凍対象の圧縮バイナリ情報を設定します.
     * @return byte[] 解凍されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertGZIPByBinary( byte[] zbin )
        throws IllegalArgumentException,IOException
    {
        if( zbin == null ){
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        return this.convertGZIPByBinary( zbin,0,zbin.length ) ;
    }
    
    /**
     * 指定された圧縮バイナリ情報を解凍.
     * <BR><BR>
     * 指定された圧縮バイナリ情報を解凍します.
     * <BR>
     * @param zbin 解凍対象の圧縮バイナリ情報を設定します.
     * @param length 解凍情報長を設定します.
     * @return byte[] 解凍されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertGZIPByBinary( byte[] zbin,int length )
        throws IllegalArgumentException,IOException
    {
        if( zbin == null ){
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        return this.convertGZIPByBinary( zbin,0,length ) ;
    }
    
    /**
     * 指定された圧縮バイナリ情報を解凍.
     * <BR><BR>
     * 指定された圧縮バイナリ情報を解凍します.
     * <BR>
     * @param zbin 解凍対象の圧縮バイナリ情報を設定します.
     * @param offset 解凍情報開始位置を設定します.
     * @param length 解凍情報長を設定します.
     * @return byte[] 解凍されたバイナリ情報が返されます.
     * @exception IllegalArgumentException 入力例外.
     * @exception IOException アクセス例外.
     */
    public final byte[] convertGZIPByBinary( byte[] zbin,int offset,int length )
        throws IllegalArgumentException,IOException
    {
        int len ;
        
        ByteArrayInputStream bIn = null ;
        ByteArrayOutputStream bOut = null ;
        GZIPInputStream zip = null ;
        BufferedInputStream buf = null ;
        byte[] tmp = null ;
        byte[] ret = null ;
        
        if(
            zbin == null || ( len = zbin.length ) <= 0 ||
            offset < 0 || offset > len ||
            length <= 0 || length > len ||
            ( offset + length ) > len
            
        )
        {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        
        try{
            
            bIn = new ByteArrayInputStream( zbin,offset,length ) ;
            bOut = new ByteArrayOutputStream() ;
            zip = new GZIPInputStream( bIn ) ;
            buf = new BufferedInputStream( zip ) ;
            
            tmp = new byte[ GZIPBinary.BUF_LENGTH ] ;
            while( ( len = buf.read( tmp ) ) >= 0 ){
                bOut.write( tmp,0,len ) ;
            }
            
            bOut.flush() ;
            ret = bOut.toByteArray() ;
            
        }catch( IOException io ){
            throw io ;
        }finally{
            
            try{
                zip.close() ;
            }catch( Exception t ){
            }
            
            try{
                buf.close() ;
            }catch( Exception t ){
            }
            
            try{
                bIn.close() ;
            }catch( Exception t ){
            }
            
            try{
                bOut.close() ;
            }catch( Exception t ){
            }
            
            bIn = null ;
            bOut = null ;
            zip = null ;
            buf = null ;
            tmp = null ;
            
        }
        
        return ret ;
        
    }
}
