package org.maachang.comet.net;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.maachang.comet.httpd.HttpdBinary;
import org.maachang.comet.httpd.HttpdHeaders;
import org.maachang.comet.httpd.HttpdRequest;
import org.maachang.comet.httpd.engine.HttpdDef;
import org.maachang.comet.httpd.engine.HttpdUtil;
import org.maachang.util.FileUtil;
import org.maachang.util.StringUtil;

/**
 * Http受信処理.
 * <BR>
 * Httpリクエスト情報を構成するための処理です.
 * 
 * @version 2008/02/08
 * @author masahito suzuki
 * @since MaachangComet 1.08
 */
public class ReadHttpdRequest {
    
    private static final int RECV_OVER_COUNT = 32 ;
    private static final long RECV_OVER_TIMEOUT = 3L ;
    private static final String CONTENT_LENGTH = "Content-Length" ;
    private static final String TRANSFER_ENCODING = "Transfer-Encoding" ;
    private static final String CHUNKED = "chunked" ;
    private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded" ;
    private static final String CHARSET = "UTF8" ;
    private static final byte[] ENTER = new byte[] {(byte) 13, (byte) 10} ;
    private static final byte[] END_HEADER = new byte[] {
        (byte) 0x0d, (byte) 0x0a,(byte) 0x0d, (byte) 0x0a} ;
    
    private ReadHttpdRequest() {
        
    }
    
    /**
     * Httpリクエスト受信処理.
     * <BR><BR>
     * Httpリクエスト受信処理を実施します.
     * <BR>
     * @param stream Socket.getInputStreamを設定します.
     * @param seqId 対象のシーケンスIDを設定します.
     * @return HttpdRequest HttpdRequestオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public static final HttpdRequest receiveHttpRequest( InputStream stream,int seqId )
        throws Exception {
        ArrayBinary buf = new ArrayBinary() ;
        ArrayBinary chunked = null ;
        ArrayBinary chunkedHead = null ;
        boolean chunkedHeadFlag = false ;
        int chunkedLen = 0 ;
        int chunkedRecvLen = 0 ;
        boolean exitChunkedFlag = false ;
        boolean noRcv = false ;
        int endHeaderPos = -1 ;
        int noRcvCnt = RECV_OVER_COUNT ;
        int contentLength = -1 ;
        int endPlus = END_HEADER.length ;
        boolean firstFlag = false ;
        HttpdRequest ret = null ;
        boolean notReceiveFlag = false ;
        try {
            for( ;; ) {
                notReceiveFlag = false ;
                // 一番最初のヘッダ内容を取得.
                if( firstFlag == false ) {
                    if( binaryIndexOf( buf.getBinary(),ENTER,0,buf.getLength() ) != -1 ) {
                        String h = new String( buf.getBinary(),CHARSET ) ;
                        if( h.indexOf( " HTTP/" ) == -1 ) {
                            throw new IOException( "The protocol is illegal("+h+")" ) ;
                        }
                        firstFlag = true ;
                    }
                }
                // 受信処理.
                if( noRcvCnt >= RECV_OVER_COUNT || stream.available() > 0 ) {
                    int d = stream.read() ;
                    // 受信情報は存在する.
                    notReceiveFlag = true ;
                    // [chunked]で受信処理.
                    if( chunked != null ) {
                        // chunked終了.
                        if( exitChunkedFlag == true ) {
                            continue ;
                        }
                        // chunkedヘッダを受信.
                        if( chunkedHeadFlag == true ) {
                            chunkedHead.write( d ) ;
                            // chunkedの長さを検出.
                            if( chunkedHead.getLength() >= 2 && d == ( int )( ENTER[ 1 ] & 0x000000ff ) &&
                                chunkedHead.getByte( chunkedHead.getLength() - 2 ) == ( byte )ENTER[ 0 ] ) {
                                // 改行のみの情報.
                                if( chunkedHead.getLength() <= 2 ) {
                                    chunkedHead.reset() ;
                                }
                                else {
                                    // chunkedの長さを取得.
                                    chunkedLen = getChunkedLength( chunkedHead ) ;
                                    chunkedHead.reset() ;
                                    chunkedRecvLen = 0 ;
                                    chunkedHeadFlag = false ;
                                    // chunked終了.
                                    if( chunkedLen == 0 ) {
                                        exitChunkedFlag = true ;
                                    }
                                }
                            }
                        }
                        // chunked情報を受信.
                        else {
                            chunked.write( d ) ;
                            chunkedRecvLen ++ ;
                            // chunkedデータ終了.
                            if( chunkedLen <= chunkedRecvLen ) {
                                chunkedHeadFlag = true ;
                                chunkedLen = -1 ;
                                chunkedRecvLen = 0 ;
                            }
                        }
                    }
                    // 通常の受信処理.
                    else {
                        buf.write( d ) ;
                        // ヘッダ受信中で、改行情報の終端.
                        if( endHeaderPos <= -1 && buf.getLength() >= 4 && d == ( int )( ENTER[ 1 ] & 0x000000ff ) ) {
                            byte[] data = buf.getBinary() ;
                            int nowEndLength = buf.getLength() ;
                            // ヘッダ終端を示す情報.
                            if( data[ nowEndLength-4 ] == END_HEADER[ 0 ] &&
                                data[ nowEndLength-3 ] == END_HEADER[ 1 ] &&
                                data[ nowEndLength-2 ] == END_HEADER[ 2 ] &&
                                data[ nowEndLength-1 ] == END_HEADER[ 3 ] ) {
                                // 受信情報は存在せず、ヘッダ解析処理に移行.
                                notReceiveFlag = false ;
                            }
                        }
                    }
                    noRcv = true ;
                    noRcvCnt = 0 ;
                }
                else {
                    // 受信情報は存在しない.
                    notReceiveFlag = false ;
                }
                
                // 受信情報が存在しない場合.
                if( notReceiveFlag == false ) {
                    // header情報の場合.////////////////////////////////////////////
                    if( endHeaderPos <= -1 ) {
                        // 受信データの途切れを検出.
                        if( noRcv == true ) {
                            noRcv = false ;
                            noRcvCnt = 0 ;
                            byte[] data = buf.getBinary() ;
                            int endPos = binaryIndexOf( data,END_HEADER,0,buf.getLength() ) ;
                            // ヘッダ区切り情報が存在する.
                            if( endPos > -1 ) {
                                endHeaderPos = endPos + endPlus ;
                                // ヘッダ内容を解析.
                                ret = convertHttpdHeader( data,endPos ) ;
                                data = null ;
                                // [GET]の場合は、Bodyは存在しない.
                                if( HttpdDef.METHOD_GET.equals( ret.getMethod() ) ) {
                                    buf.destroy() ;
                                    break ;
                                }
                                // [POST]の場合は、コンテンツ長を取得して、Body情報を取得.
                                else if( HttpdDef.METHOD_POST.equals( ret.getMethod() ) ) {
                                    // chunkedでの受信.
                                    if( ret.getHeader().size( TRANSFER_ENCODING ) == 1 ) {
                                        String s = ret.getHeader().getHeader( TRANSFER_ENCODING ) ;
                                        if( CHUNKED.equals( s ) == false ) {
                                            throw new IOException( "不正な["+TRANSFER_ENCODING+"="+s+"]を検出しました" ) ;
                                        }
                                        contentLength = -1 ;
                                        chunked = new ArrayBinary() ;
                                        chunkedHead = new ArrayBinary() ;
                                        chunkedHeadFlag = true ;
                                    }
                                    // コンテンツ長での受信.
                                    else if( ret.getHeader().size( CONTENT_LENGTH ) == 1 ) {
                                        String s = ret.getHeader().getHeader( CONTENT_LENGTH ) ;
                                        if( s == null || s.length() <= 0 ) {
                                            throw new IOException( "不正な["+CONTENT_LENGTH+"]を検出しました" ) ;
                                        }
                                        // コンテンツ長を取得.
                                        contentLength = Integer.parseInt( s ) ;
                                        if( contentLength >= ArrayBinary.MAX_LENGTH ) {
                                            throw new IOException( "受信データ長が最大値["+ArrayBinary.MAX_LENGTH+"]を越しています" ) ;
                                        }
                                        else if( contentLength <= -1 ) {
                                            throw new IOException( "受信データ長["+contentLength+"]は不正です" ) ;
                                        }
                                    }
                                    // Body処理に移行する.
                                    noRcv = true ;
                                    noRcvCnt = 0 ;
                                }
                                // その他のメソッド.
                                else {
                                    throw new IOException( "不正なメソッド[" + ret.getMethod() + "]を検出しました" ) ;
                                }
                            }
                        }
                        // 受信データが存在しない状況が続く場合.
                        else {
                            noRcvCnt ++ ;
                            Thread.sleep( RECV_OVER_TIMEOUT ) ;
                        }
                    }
                    // body情報の場合.//////////////////////////////////////////////
                    else {
                        // 受信データの途切れを検出.
                        if( noRcv == true ) {
                            noRcv = false ;
                            noRcvCnt = 0 ;
                            // chunkedデータの終端.
                            if( exitChunkedFlag == true && chunked != null ) {
                                if( chunked.getLength() > 0 ) {
                                    byte[] body = chunked.getBinary( chunked.getLength() ) ;
                                    buf.destroy() ;
                                    chunked.destroy() ;
                                    // Body変換.
                                    convertBody( ret,body ) ;
                                    body = null ;
                                }
                                break ;
                            }
                            // データの終端.
                            else if( contentLength != -1 && contentLength <= buf.getLength() - endHeaderPos ) {
                                // body情報を生成.
                                int bodyLen = buf.getLength() - endHeaderPos ;
                                if( bodyLen > 0 ) {
                                    byte[] body = new byte[ bodyLen ] ;
                                    System.arraycopy( buf.getBinary(),endHeaderPos,body,0,bodyLen ) ;
                                    buf.destroy() ;
                                    // Body変換.
                                    convertBody( ret,body ) ;
                                    body = null ;
                                }
                                break ;
                            }
                        }
                        // 受信データが存在しない状況が続く場合.
                        else {
                            noRcvCnt ++ ;
                            Thread.sleep( RECV_OVER_TIMEOUT ) ;
                        }
                    }
                }
            }
        } catch( Exception e ) {
            throw e ;
        }
        return ret ;
    }
    
    /**
     * バイナリ検索.
     */
    private static int binaryIndexOf( byte[] binary,String data,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 ||
            data == null || data.length() <= 0 ) {
            return -1 ;
        }
        return binaryIndexOf( binary,data.getBytes( CHARSET ),off,len ) ;
    }
    
    /**
     * バイナリ検索.
     */
    private static int binaryIndexOf( byte[] binary,byte[] data,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 ||
            data == null || data.length <= 0 ) {
            return -1 ;
        }
        int dataLen = data.length ;
        if( len <= 0 || len >= binary.length ) {
            len = binary.length ;
        }
        int ret = -1 ;
        for( int i = off ; i < len ; i ++ ) {
            if( binary[ i ] == data[ 0 ] && i+dataLen <= len ) {
                ret = i ;
                for( int j = 1 ; j < dataLen ; j ++ ) {
                    if( binary[ i+j ] != data[ j ] ) {
                        ret = -1 ;
                        break ;
                    }
                }
                if( ret != -1 ) {
                    return ret ;
                }
            }
        }
        return -1 ;
    }
    
    /**
     * HTTPヘッダを解析.
     */
    private static final HttpdRequest convertHttpdHeader( byte[] binary,int endPos )
        throws Exception {
        HttpdBaseRequestImpl ret = new HttpdBaseRequestImpl() ;
        int p = 0 ;
        int b = 0 ;
        int line = 0 ;
        int enterLen = ENTER.length ;
        // ヘッダ情報を読み込む.
        for( ;; ) {
            p = binaryIndexOf( binary,ENTER,b,endPos ) ;
            if( p == -1 ) {
                if( b >= endPos ) {
                    break ;
                }
                p = endPos ;
            }
            String one = new String( binary,b,p-b,CHARSET ) ;
            b = p + enterLen ;
            
            // Method情報を取得.
            if( line == 0 ) {
                convertHttpExecutionAndVersion( ret,one ) ;
                if( ret.getHttpVersion().endsWith( HttpdDef.VERSION_09 ) == true ) {
                    // HTTPバージョンが0.9の場合.
                    convertAnalysisQuery( ret ) ;// パラメータを解析して取得.
                    return ret ;
                }
            }
            // ヘッダ解析.
            else {
                convertHttpHeader( ret,one ) ;
            }
            // ヘッダ終端を検出.
            if( p >= endPos ) {
                break ;
            }
            line ++ ;
        }
        convertAnalysisQuery( ret ) ;// URLパラメータ解析.
        return ret ;
    }
    /**
     * 処理タイプと、HTTPバージョンを取得.
     */
    private static final void convertHttpExecutionAndVersion( HttpdRequest out,String oneLine ) {
        int pos1 = oneLine.indexOf( " " ) ;
        int pos2 = oneLine.indexOf( " ",pos1+1 ) ;
        String exec = oneLine.substring( 0,pos1 ) ;
        String url = oneLine.substring( pos1+1,pos2 ) ;
        String version = oneLine.substring( pos2+1,oneLine.length() ) ;
        out.setMethod( exec.trim() ) ;
        out.setUrlPath( url.trim() ) ;
        out.setHttpVersion( version.trim() ) ;
    }
    
    /**
     * パラメータを解析.
     */
    private static final void convertHttpHeader( HttpdRequest out,String oneLine ) {
        int p = oneLine.indexOf( ":" ) ;
        if( p == -1 ) {
            return ;
        }
        String key = oneLine.substring( 0,p ).trim() ;
        p += 1 ;
        oneLine = oneLine.substring( p+1,oneLine.length() ) ;
        if( oneLine == null || ( oneLine = oneLine.trim() ).length() <= 0 ) {
            out.getHeader().addHeader( key,null ) ;
        }
        else if( key.indexOf( "User-Agent" ) != -1 ||
            key.indexOf( "Modified" ) != -1 ||
            key.indexOf( "Date" ) != -1 ) {
            out.getHeader().addHeader( key,oneLine ) ;
        }
        else {
            if( oneLine.indexOf( "," ) != -1 ) {
                ArrayList<String> lst = StringUtil.cutString( oneLine,"," ) ;
                int len = lst.size() ;
                HttpdHeaders header = out.getHeader() ;
                for( int i = 0 ; i < len ; i ++ ) {
                    if( i == 0 ) {
                        String o = lst.get( i ).trim() ;
                        if( o.length() > 0 ) {
                            int sp = o.indexOf( " " ) ;
                            if( sp != -1 ) {
                                boolean flg = false ;
                                for( int j = 0 ; j < sp ; j ++ ) {
                                    char c = o.charAt( j ) ;
                                    if( c == '\"' || c == '\'' ) {
                                        flg = true ;
                                        break ;
                                    }
                                }
                                if( flg == true ) {
                                    header.addHeader( key,o ) ;
                                }
                                else {
                                    header.addHeader( key,o.substring( 0,sp ).trim() ) ;
                                    header.addHeader( key,o.substring( sp+1 ).trim() ) ;
                                }
                            }
                            else {
                                header.addHeader( key,o ) ;
                            }
                        }
                    }
                    else {
                        String o = lst.get( i ).trim() ;
                        if( o.length() > 0 ) {
                            header.addHeader( key,o ) ;
                        }
                    }
                }
            }
            else {
                oneLine = oneLine.trim() ;
                if( oneLine.length() > 0 ) {
                    out.getHeader().addHeader( key,oneLine ) ;
                }
            }
        }
    }
    
    /**
     * URLクエリパラメータを解析して、パラメータとして取得.
     * <BR>
     * @param out 対象のリクエスト情報を設定します.
     */
    public static final void convertAnalysisQuery( HttpdRequest out ) {
        String query = HttpdUtil.getQueryValue( out.getUrlPath() ) ;
        if( query != null && ( query = query.trim() ).length() > 0) {
            out.setUrlPath( HttpdUtil.getReadName( out.getUrlPath() ) ) ;
            HttpdUtil.convertQueryByParams( out.getQuery(),query,"UTF8" ) ;
        }
    }
    
    /**
     * 解析されたBodyデータをパラメータとして取得.
     */
    private static final void convertAnalysisBody( HttpdRequest out,String body ) {
        if( body != null && ( body = body.trim() ).length() > 0 ) {
            HttpdUtil.convertQueryByParams( out.getQuery(),body,"UTF8" ) ;
        }
    }
    
    /**
     * POST情報での、Body内容を解析.
     * <BR>
     * @param out 対象のHTTPリクエストを設定します.
     * @param binary 対象のバイナリを設定します.
     * @exception Exception 例外.
     */
    public static final void convertBody( HttpdRequest out,byte[] binary )
        throws Exception {
        int p = 0 ;
        int b = 0 ;
        int enterLen = ENTER.length ;
        int len = out.getHeader().size( "Content-Type" ) ;
        String multiPartForm = null ;
        String dataType = null ;
        for( int i = 0 ; i < len ; i ++ ) {
            String contentType = out.getHeader().getHeader( "Content-Type",i ) ;
            if( multiPartForm == null ) {
                int boundary = -1 ;
                if( ( boundary = contentType.indexOf( "multipart/form-data" ) ) != -1 &&
                    ( boundary = contentType.indexOf( "boundary=",boundary ) ) != -1 ) {
                    multiPartForm = contentType.substring( boundary+"boundary=".length(),contentType.length() ) ;
                }
            }
            if( dataType == null || contentType.startsWith( FORM_CONTENT_TYPE ) == false ) {
                dataType = contentType ;
            }
        }
        // POSTに[Content-Type]が存在しない場合.
        if( dataType == null ) {
            // [application/x-www-form-urlencoded]であることにする(IEのAjax対応).
            dataType = FORM_CONTENT_TYPE ;
        }
        // HTTPがMultipartFormの場合.
        if( multiPartForm != null ) {
            out.setBody( null ) ;// body情報は保持しない.
            String target = "--" + multiPartForm ;
            String end = target + "--" ;
            String keyName = null ;
            String fileName = null ;
            String mimeType = null ;
            boolean dataFlag = false ;
            boolean keyFlag = false ;
            int filenameLen = "; filename=\"".length() ;
            int nameLen = "; name=\"".length() ;
            for( ;; ) {
                // データ条件で無い場合.
                if( dataFlag == false ) {
                    p = binaryIndexOf( binary,ENTER,b,binary.length ) ;
                    if( p == -1 ) {
                        break ;
                    }
                    else if( p == b ) {
                        b += enterLen ;
                        continue ;
                    }
                    String one = new String( binary,b,p-b,CHARSET ) ;
                    b = p + enterLen ;
                    // データ開始値.
                    if( keyFlag == false ) {
                        // データの開始値の場合.
                        if( one.equals( target ) == true ) {
                            keyFlag = true ;
                        }
                        // 全てのデータ終了値の場合.
                        else if( one.equals( end ) == true ) {
                            break ;
                        }
                        // その他.
                        else {
                            throw new IllegalArgumentException( "不正な条件を検出しました" ) ;
                        }
                    }
                    // データキー名など.
                    else {
                        // データ情報の場合.
                        if( one.length() <= 0 ) {
                            dataFlag = true ;
                        }
                        // データキー名などの情報.
                        else {
                            // アップロードファイル名.
                            if( one.startsWith( "Content-Disposition:" ) ) {
                                int filePnt = one.indexOf( "; filename=\"" ) ;
                                if( filePnt != -1 ) {
                                    filePnt+= filenameLen ;
                                    String fullPath = one.substring( filePnt,one.indexOf( "\"",filePnt ) ).trim() ;
                                    fileName = FileUtil.getFileName( fullPath ).trim() ;
                                }
                            }
                            // データキー名.
                            int namePnt = one.indexOf( "; name=\"" ) ;
                            if( namePnt != -1 ) {
                                namePnt+=nameLen ;
                                keyName = one.substring( namePnt,one.indexOf( "\"",namePnt ) ).trim() ;
                            }
                            // MimeType.
                            else if( one.startsWith( "Content-Type:" ) ) {
                                mimeType = one.substring( "Content-Type:".length(),one.length() ).trim() ;
                            }
                            // その他.
                            else {
                                throw new IllegalArgumentException( "不正な条件を検出しました" ) ;
                            }
                        }
                    }
                }
                // データ情報.
                else {
                    // uploadBinary情報.
                    if( fileName != null ) {
                        p = binaryIndexOf( binary,target,b,binary.length ) ;
                        if( p == -1 ) {
                            throw new IllegalArgumentException( "不正な条件を検出しました" ) ;
                        }
                        if( fileName == null || ( fileName = fileName.trim() ).length() <= 0 ) {
                            b = p ;
                        }
                        else {
                            len = p-(b+enterLen) ;
                            byte[] uploadBinary = new byte[ len ] ;
                            System.arraycopy( binary,b,uploadBinary,0,len ) ;
                            b = p ;
                            HttpdBinary bin = new HttpdBinary( fileName,mimeType,uploadBinary ) ;
                            out.getQuery().addParam( keyName,bin ) ;
                        }
                    }
                    else {
                        p = binaryIndexOf( binary,target,b,binary.length ) ;
                        if( p == -1 ) {
                            throw new IllegalArgumentException( "不正な条件を検出しました" ) ;
                        }
                        String one = new String( binary,b,p-(b+enterLen),CHARSET ) ;
                        b = p ;
                        out.getQuery().addParam( keyName,one ) ;
                    }
                    keyName = null ;
                    fileName = null ;
                    mimeType = null ;
                    dataFlag = false ;
                    keyFlag = false ;
                }
            }
        }
        // 通常のForm情報の場合.
        else if( dataType != null && dataType.startsWith( FORM_CONTENT_TYPE ) == true ) {
            String charset = getCharset( dataType ) ;
            // body情報を保持.
            out.setBody( binary ) ;
            boolean end = false ;
            StringBuilder buf = new StringBuilder() ;
            for( ;; ) {
                if( end == true ) {
                    break ;
                }
                p = binaryIndexOf( binary,ENTER,b,binary.length ) ;
                if( p <= -1 ) {
                    p = binary.length ;
                    end = true ;
                }
                if( p == b ) {
                    b += enterLen ;
                    continue ;
                }
                String one = new String( binary,b,p-b,charset ) ;
                one = StringUtil.changeString( one,"\r","" ) ;
                one = StringUtil.changeString( one,"\n","" ) ;
                b = p + enterLen ;
                
                // データ終端を検出.
                if( one.length() <= 0 ) {
                    break ;
                }
                buf.append( one ) ;
            }
            String bodys = buf.toString() ;
            buf = null ;
            convertAnalysisBody( out,bodys ) ;
            bodys = null ;
        }
        // その他のForm情報の場合.
        else {
            // データは区切らない.
            out.setBody( binary ) ;
        }
    }
    
    private static final String getCharset( String contentType ) {
        int p = contentType.indexOf( "charset=" ) ;
        if( p <= -1 ) {
            return CHARSET ;
        }
        else {
            return contentType.substring( p+"charset=".length() ) ;
        }
    }
    
    private static final int getChunkedLength( ArrayBinary buf )
        throws Exception {
        ArrayBinary t = new ArrayBinary() ;
        int len = buf.getLength() ;
        for( int i = 0 ; i < len ; i ++ ) {
            int d = ( int )( buf.getByte( i ) & 0x000000ff ) ;
            if( d == 0 ) {
                continue ;
            }
            t.write( d ) ;
        }
        String s = new String( t.getBinary(),0,t.getLength()-2,"ISO-8859-1" ) ;
        return Integer.parseInt( s,16 ) ;
    }
}

class ArrayBinary {
    protected static final int MAX_LENGTH = 32 * 0x00100000 ;
    private static final int BUFFER = 4096 ;
    private byte[] binary = null ;
    private int length = 0 ;
    
    public ArrayBinary() {
        this.binary = new byte[ BUFFER ] ;
        this.length = 0 ;
    }
    
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    public void destroy() {
        this.binary = null ;
        this.length = 0 ;
    }
    
    public void reset() {
        if( this.binary.length == BUFFER ) {
            this.length = 0 ;
        }
        else {
            this.binary = new byte[ BUFFER ] ;
            this.length = 0 ;
        }
    }
    
    public void write( int b ) throws Exception {
        if( length >= MAX_LENGTH ) {
            this.destroy() ;
            throw new IOException( "受信データ長が最大値["+MAX_LENGTH+"]を越しています" ) ;
        }
        if( binary.length <= length ) {
            byte[] t = binary ;
            int iLen = t.length * 2 ;
            if( iLen >= MAX_LENGTH ) {
                iLen = MAX_LENGTH ;
            }
            binary = new byte[ iLen ] ;
            System.arraycopy( t,0,binary,0,t.length ) ;
            t = null ;
        }
        binary[ length ] = ( byte )( b & 0x000000ff ) ;
        length ++ ;
    }
    
    public byte getByte( int no ) {
        return binary[ no ] ;
    }
    
    public byte[] getBinary() {
        return binary ;
    }
    
    public byte[] getBinary( int length ) {
        if( length <= 0 ) {
            length = this.length ;
        }
        if( this.length < length ) {
            length = this.length ;
        }
        byte[] ret = new byte[ length ] ;
        System.arraycopy( this.binary,0,ret,0,length ) ;
        return ret ;
    }
    
    public int getLength() {
        return length ;
    }
}
