package org.maachang.mimdb.core.impl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.SoftReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.maachang.mimdb.core.util.Stack;
import org.maachang.mimdb.core.util.StringUtils;

/**
 * ユーティリティ.
 * 
 * @version 2013/10/13
 * @author masahito suzuki
 * @since MasterInMemDB 1.00
 */
@SuppressWarnings("unchecked")
public final class MimdbUtils {
    private MimdbUtils() {}
    
    // java.sql.Date文字変換用.
    private static final ThreadLocal<SoftReference<Object[]>> sqlDateFormatLocal =
        new ThreadLocal<SoftReference<Object[]>>() ;
    
    // java.sql.Time文字変換用.
    private static final ThreadLocal<SoftReference<Object[]>> sqlTimeFormatLocal =
        new ThreadLocal<SoftReference<Object[]>>() ;
    
    // java.sql.Timestamp文字変換用.
    private static final ThreadLocal<SoftReference<Object[]>> sqlTimestampFormatLocal =
        new ThreadLocal<SoftReference<Object[]>>() ;
    
    /** java.sql.Dateフォーマットを取得. **/
    private static final Object[] getDateFormat() {
        Object[] ret ;
        SoftReference<Object[]> o = sqlDateFormatLocal.get() ;
        if( o == null || ( ret = o.get() ) == null ) {
            // java.sql.Date文字変換用.
            SimpleDateFormat[] fmt = new SimpleDateFormat[ 2 ] ;
            int[] len = new int[ 2 ] ;
            try {
                fmt[ 0 ] = new SimpleDateFormat( "yyyy-MM-dd" ) ;
                fmt[ 1 ] = new SimpleDateFormat( "yyyy/MM/dd" ) ;
                len[ 0 ] = 10 ;
                len[ 1 ] = 10 ;
            } catch( Exception e ) {
                fmt = null ;
                len = null ;
            }
            
            ret = new Object[]{ fmt,len } ;
            sqlDateFormatLocal.set( new SoftReference<Object[]>( ret ) ) ;
        }
        return ret ;
    }
    
    /** java.sql.Timeフォーマットを取得. **/
    private static final Object[] getTimeFormat() {
        Object[] ret ;
        SoftReference<Object[]> o = sqlTimeFormatLocal.get() ;
        if( o == null || ( ret = o.get() ) == null ) {
            // java.sql.Time文字変換用.
            SimpleDateFormat[] fmt = new SimpleDateFormat[ 2 ] ;
            int[] len = new int[ 2 ] ;
            try {
                fmt[ 0 ] = new SimpleDateFormat( "hh:mm:ss" ) ;
                fmt[ 1 ] = new SimpleDateFormat( "hh:mm" ) ;
                len[ 0 ] = 8 ;
                len[ 1 ] = 5 ;
            } catch( Exception e ) {
                fmt = null ;
                len = null ;
            }
            
            ret = new Object[]{ fmt,len } ;
            sqlTimeFormatLocal.set( new SoftReference<Object[]>( ret ) ) ;
        }
        return ret ;
    }
    
    /** java.sql.Timestampフォーマットを取得. **/
    private static final Object[] getTimestampFormat() {
        Object[] ret ;
        SoftReference<Object[]> o = sqlTimestampFormatLocal.get() ;
        if( o == null || ( ret = o.get() ) == null ) {
            // java.sql.Timestamp文字変換用.
            SimpleDateFormat[] fmt = new SimpleDateFormat[ 4 ] ;
            int[] len = new int[ 4 ] ;
            try {
                fmt[ 0 ] = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" ) ;
                fmt[ 1 ] = new SimpleDateFormat( "yyyy-MM-dd hh:mm" ) ;
                fmt[ 2 ] = new SimpleDateFormat( "yyyy/MM/dd hh:mm:ss" ) ;
                fmt[ 3 ] = new SimpleDateFormat( "yyyy/MM/dd hh:mm" ) ;
                len[ 0 ] = 19 ;
                len[ 1 ] = 16 ;
                len[ 2 ] = 19 ;
                len[ 3 ] = 16 ;
            } catch( Exception e ) {
                fmt = null ;
                len = null ;
            }
            
            ret = new Object[]{ fmt,len } ;
            sqlTimestampFormatLocal.set( new SoftReference<Object[]>( ret ) ) ;
        }
        return ret ;
    }
    
    /**
     * 文字列内容が数値かチェック.
     * @param num 対象の文字列を設定します.
     * @return boolean [true]の場合、文字列内は数値が格納されています.
     */
    public static final boolean isNumeric( String num ) {
        if( num == null || ( num = num.trim() ).length() <= 0 ) {
            return false ;
        }
        int start = 0 ;
        if( num.charAt( 0 ) == '-' ) {
            start = 1 ;
        }
        boolean dt = false ;
        int len = num.length() ;
        if( start < len ) {
            for( int i = start ; i < len ; i ++ ) {
                char c = num.charAt( i ) ;
                if( c == '.' ) {
                    if( dt ) {
                        return false ;
                    }
                    dt = true ;
                }
                else if( ( c >= '0' && c <= '9' ) == false ) {
                    return false ;
                }
            }
        }
        else {
            return false ;
        }
        return true ;
    }
    
    /**
     * Boolean変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final Boolean convertBool( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof Boolean ) {
            return (Boolean)o ;
        }
        if( o instanceof Number ) {
            return ( ( ( Number )o ).intValue() == 0 ) ? false : true ;
        }
        if( o instanceof String ) {
            String s = ( (String)o ).trim().toLowerCase() ;
            if( "true".equals( s ) || "t".equals( s ) ) {
                return true ;
            }
            else if( "false".equals( s ) || "f".equals( s ) ) {
                return false ;
            }
        }
        throw new IOException( "BOOL型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * Integer変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final Integer convertInt( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof Integer ) {
            return (Integer)o ;
        }
        else if( o instanceof Number ) {
            return ((Number)o).intValue() ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            if( isNumeric( s ) ) {
                return StringUtils.parseInt( s ) ;
            }
        }
        throw new IOException( "Int型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * Long変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final Long convertLong( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof Long ) {
            return (Long)o ;
        }
        else if( o instanceof Number ) {
            return ((Number)o).longValue() ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            if( isNumeric( s ) ) {
                return StringUtils.parseLong( s.trim() ) ;
            }
        }
        throw new IOException( "Long型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * Double変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final Double convertDouble( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof Double ) {
            return (Double)o ;
        }
        else if( o instanceof Number ) {
            return ((Number)o).doubleValue() ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            if( isNumeric( s ) ) {
                return StringUtils.parseDouble( s.trim() ) ;
            }
        }
        throw new IOException( "Double型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 文字列変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final String convertString( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof String ) {
            return (String)o ;
        }
        return o.toString() ;
    }
    
    
    /** 日付のみ表現. **/
    private static final java.sql.Date _cDate( final long d ) {
        return _cDate( new java.util.Date( d ) ) ;
    }
    private static final java.sql.Date _cDate( java.util.Date n ) {
        return new java.sql.Date( n.getYear(),n.getMonth(),n.getDate() ) ;
    }
    
    /**
     * 日付変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.sql.Date convertSqlDate( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof java.util.Date ) {
            return _cDate( ( ( java.util.Date )o ) ) ;
        }
        else if( o instanceof Long ) {
            return _cDate( (Long)o ) ;
        }
        else if( o instanceof Number ) {
            return _cDate( ((Number)o).longValue() ) ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            if( isNumeric( s ) ) {
                return _cDate( StringUtils.parseLong( s ) ) ;
            }
            else if( s.length() == 10 ) {
                java.util.Date d ;
                
                Object[] n = getDateFormat() ;
                SimpleDateFormat[] fmt = (SimpleDateFormat[])n[ 0 ] ;
                int[] fmtLen = (int[])n[ 1 ] ;
                int len = fmt.length ;
                
                for( int i = 0 ; i < len ; i ++ ) {
                    if( fmtLen[ i ] == s.length() ) {
                        try {
                            d = fmt[ i ].parse( s ) ;
                            return _cDate( d.getTime() ) ;
                        } catch( Exception e ) {
                        }
                    }
                }
            }
        }
        throw new IOException( "java.sql.Date型変換に失敗しました[" + o + "]" ) ;
    }
    
    /** 時間のみ表現. **/
    private static final java.sql.Time _cTime( final long d ) {
        return _cTime( new java.util.Date( d ) ) ;
    }
    private static final java.sql.Time _cTime( java.util.Date n ) {
        return new java.sql.Time( n.getHours(),n.getMinutes(),n.getSeconds() ) ;
    }
    
    /**
     * 時間変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.sql.Time convertSqlTime( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof java.util.Date ) {
            return _cTime( ( java.util.Date )o ) ;
        }
        else if( o instanceof Long ) {
            return _cTime( (Long)o ) ;
        }
        else if( o instanceof Number ) {
            return _cTime( ((Number)o).longValue() ) ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            int sLen = s.length() ;
            if( isNumeric( s ) ) {
                return _cTime( StringUtils.parseLong( s ) ) ;
            }
            else if( ( sLen == 8 || sLen == 5 ) ) {
                java.util.Date d ;
                
                Object[] n = getTimeFormat() ;
                SimpleDateFormat[] fmt = (SimpleDateFormat[])n[ 0 ] ;
                int[] fmtLen = (int[])n[ 1 ] ;
                int len = fmt.length ;
                
                for( int i = 0 ; i < len ; i ++ ) {
                    if( fmtLen[ i ] == s.length() ) {
                        try {
                            d = fmt[ i ].parse( s ) ;
                            return _cTime( d.getTime() ) ;
                        } catch( Exception e ) {
                        }
                    }
                }
            }
        }
        throw new IOException( "java.sql.Time型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 日付時間変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.sql.Timestamp convertSqlTimestamp( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof java.util.Date ) {
            if( o instanceof java.sql.Timestamp ) {
                return ( java.sql.Timestamp )o ;
            }
            return new java.sql.Timestamp( ( ( java.util.Date )o ).getTime() ) ;
        }
        else if( o instanceof Long ) {
            return new java.sql.Timestamp( (Long)o ) ;
        }
        else if( o instanceof Number ) {
            return new java.sql.Timestamp( ((Number)o).longValue() ) ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            int sLen = s.length() ;
            if( isNumeric( s ) ) {
                return new java.sql.Timestamp( StringUtils.parseLong( s ) ) ;
            }
            else if( ( sLen == 19 || sLen == 16 ) ) {
                java.util.Date d ;
                
                Object[] n = getTimestampFormat() ;
                SimpleDateFormat[] fmt = (SimpleDateFormat[])n[ 0 ] ;
                int[] fmtLen = (int[])n[ 1 ] ;
                int len = fmt.length ;
                
                for( int i = 0 ; i < len ; i ++ ) {
                    if( fmtLen[ i ] == s.length() ) {
                        try {
                            d = fmt[ i ].parse( s ) ;
                            return new java.sql.Timestamp( d.getTime() ) ;
                        } catch( Exception e ) {
                        }
                    }
                }
            }
        }
        throw new IOException( "java.sql.Timestamp型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 通常日付変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.util.Date convertDate( final Object o )
        throws Exception {
        if( o == null ) {
            return null ;
        }
        if( o instanceof java.util.Date ) {
            return ( java.util.Date )o ;
        }
        else if( o instanceof Long ) {
            return new java.util.Date( (Long)o ) ;
        }
        else if( o instanceof Number ) {
            return new java.util.Date( ((Number)o).longValue() ) ;
        }
        else if( o instanceof String ) {
            String s = ((String)o).trim() ;
            if( isNumeric( s ) ) {
                return new java.util.Date( StringUtils.parseLong( s ) ) ;
            }
            else {
                java.util.Date d = null ;
                SimpleDateFormat[] fmt = null ;
                int[] fmtLen = null ;
                int len = -1 ;
                
                int sLen = s.length() ;
                // timestamp.
                if( ( sLen == 19 || sLen == 16 ) ) {
                    
                    Object[] n = getTimestampFormat() ;
                    fmt = (SimpleDateFormat[])n[ 0 ] ;
                    fmtLen = (int[])n[ 1 ] ;
                    len = fmt.length ;
                    
                    if( fmt != null ) {
                        for( int i = 0 ; i < len ; i ++ ) {
                            if( fmtLen[ i ] == s.length() ) {
                                try {
                                    d = fmt[ i ].parse( s ) ;
                                    return new java.sql.Timestamp( d.getTime() ) ;
                                } catch( Exception e ) {
                                }
                            }
                        }
                    }
                }
                // date.
                else if( sLen == 10 ) {
                    
                    Object[] n = getDateFormat() ;
                    fmt = (SimpleDateFormat[])n[ 0 ] ;
                    fmtLen = (int[])n[ 1 ] ;
                    len = fmt.length ;
                    
                    if( fmt != null ) {
                        for( int i = 0 ; i < len ; i ++ ) {
                            if( fmtLen[ i ] == s.length() ) {
                                try {
                                    d = fmt[ i ].parse( s ) ;
                                    return _cDate( d.getTime() ) ;
                                } catch( Exception e ) {
                                }
                            }
                        }
                    }
                }
                // time.
                else if( ( sLen == 8 || sLen == 5 ) ) {
                    
                    Object[] n = getTimeFormat() ;
                    fmt = (SimpleDateFormat[])n[ 0 ] ;
                    fmtLen = (int[])n[ 1 ] ;
                    len = fmt.length ;
                    
                    if( fmt != null ) {
                        for( int i = 0 ; i < len ; i ++ ) {
                            if( fmtLen[ i ] == s.length() ) {
                                try {
                                    d = fmt[ i ].parse( s ) ;
                                    return _cTime( d.getTime() ) ;
                                } catch( Exception e ) {
                                }
                            }
                        }
                    }
                }
                
            }
        }
        throw new IOException( "java.util.Date型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchInt(final int[] a,final int key) {
        int low = 0 ;
        int high = a.length - 1 ;
        int mid,midVal ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                low = mid + 1;
            }
            else if (midVal > key) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        return -1 ; // key not found.
    }
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchLong(final long[] a,final long key) {
        int low = 0;
        int high = a.length -1 ;
        int mid ;
        long midVal ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                low = mid + 1 ;
            }
            else if (midVal > key) {
                high = mid - 1 ;
            }
            else {
                return mid ; // key found
            }
        }
        return -1 ;  // key not found.
    }
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchDouble(final double[] a, final double key) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        double midVal ;
        long midBits,keyBits ;
        keyBits = Double.doubleToLongBits( key ) ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                cmp = -1;   // Neither val is NaN, thisVal is smaller
            } else if (midVal > key) {
                cmp = 1;    // Neither val is NaN, thisVal is larger
            } else {
                midBits = Double.doubleToLongBits( midVal ) ;
                cmp = (midBits == keyBits ?  0 : // Values are equal
                       (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
                        1));                     // (0.0, -0.0) or (NaN, !NaN)
            }
            if (cmp < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        return -1 ;
    }
    
    /**
     * 完全一致検索.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchObject(final Object[] a,final Object key) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            if (( cmp = ( (Comparable)a[mid] ).compareTo(key) ) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        return -1 ;
    }
    
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchIntBS( final boolean big,final int[] a,final int key ) {
        int low = 0 ;
        int high = a.length - 1 ;
        int mid,midVal ;
        mid = 0 ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                low = mid + 1;
            }
            else if (midVal > key) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        if( big ) {
            if( a[mid] > key ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( a[mid] > key ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
   
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchLongBS( final boolean big,final long[] a,final long key ) {
        int low = 0;
        int high = a.length -1 ;
        int mid = 0 ;
        long midVal ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                low = mid + 1 ;
            }
            else if (midVal > key) {
                high = mid - 1 ;
            }
            else {
                return mid ; // key found
            }
        }
        if( big ) {
            if( a[mid] > key ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( a[mid] > key ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
    
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchDoubleBS( final boolean big,final double[] a, final double key ) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        double midVal ;
        long midBits,keyBits ;
        keyBits = Double.doubleToLongBits( key ) ;
        mid = 0 ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            midVal = a[mid];
            if (midVal < key) {
                cmp = -1;   // Neither val is NaN, thisVal is smaller
            } else if (midVal > key) {
                cmp = 1;    // Neither val is NaN, thisVal is larger
            } else {
                midBits = Double.doubleToLongBits( midVal ) ;
                cmp = (midBits == keyBits ?  0 : // Values are equal
                       (midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
                        1));                     // (0.0, -0.0) or (NaN, !NaN)
            }
            if (cmp < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        if( big ) {
            if( a[mid] > key ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( a[mid] > key ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
    
    /**
     * 大なり小なり検索.
     * @param big [true]の場合、第三引数が第二引数の検知条件より大きい条件(>=)を対象とします.
     * @param a 対象の配列を設定します.
     * @param key 対象のキーを設定します.
     * @return int 項番が返却されます.
     *             [-1]が返却された場合、情報は存在しません.
     */
    public static final int searchObjectBS( final boolean big,final Object[] a,final Object key ) {
        int low = 0 ;
        int high = a.length -1 ;
        int mid,cmp ;
        mid = 0 ;
        while (low <= high) {
            mid = (low + high) >>> 1;
            if (( cmp = ( (Comparable)a[mid] ).compareTo(key) ) < 0) {
                low = mid + 1;
            }
            else if (cmp > 0) {
                high = mid - 1;
            }
            else {
                return mid; // key found
            }
        }
        if( big ) {
            if( ( (Comparable)a[mid] ).compareTo( key ) > 0 ) {
                return mid ;
            }
            else if( a.length <= mid + 1 ) {
                return a.length-1 ;
            }
            return mid + 1 ;
        }
        if( ( (Comparable)a[mid] ).compareTo( key ) > 0 ) {
            if( mid <= 0 ) {
                return 0 ;
            }
            return mid -1 ;
        }
        return mid ;
    }
    
    /**
     * ファイルリストを取得.
     * @param name 対象のファイル名を設定します.
     * @param charset 対象の文字コードを設定します.
     * @return List<String> リストが返却されます.
     * @exception Exception 例外.
     */
    public static final List<String> getList( final String name,String charset ) throws Exception {
        if( charset == null || ( charset = charset.trim() ).length() <= 0 ) {
            charset = "UTF8" ;
        }
        List<String> ret = null ;
        BufferedReader r = new BufferedReader( new InputStreamReader( new FileInputStream( name ),charset ) ) ;
        try {
            ret = new ArrayList<String>() ;
            String n ;
            while( ( n = r.readLine() ) != null ) {
                if( ( n = n.trim() ).length() <= 0 ) {
                    continue ;
                }
                ret.add( n ) ;
            }
            r.close() ;
            r = null ;
        } finally {
            if( r != null ) {
                try {
                    r.close() ;
                } catch( Exception e ) {
                }
            }
        }
        return ret ;
    }
    
    /**
     * 指定文字内のコーテーションインデントを1つ上げる.
     * @param string 対象の文字列を設定します.
     * @param off 対象のオフセット値を設定します.
     * @param dc [true]の場合、ダブルコーテーションで処理します.
     * @return String 変換された文字列が返されます.
     */
    public static final String indentCote( String string,int off,boolean dc ) {
        if( string == null || string.length() <= 0 ) {
            return string ;
        }
        char cote = '\"' ;
        if( !dc ) {
            cote = '\'' ;
        }
        if( string.indexOf( cote ) != -1 ) {
            int yenCount = 0 ;
            int indNo = off ;
            int len = string.length() ;
            StringBuilder buf = new StringBuilder() ;
            Stack<Integer> stack = new Stack<Integer>() ;
            int yenLen ;
            for( int i = 0 ; i < len ; i ++ ) {
                char c = string.charAt( i ) ;
                if( c == cote ) {
                    boolean decFlag = false ;
                    if( stack.size() > 0 && stack.peek() >= yenCount ) {
                        if( stack.peek() > yenCount ) {
                            stack.pop() ;
                            indNo -- ;
                            decFlag = false ;
                            if( indNo < 0 ) {
                                indNo = 0 ;
                            }
                        }
                        else {
                            stack.pop() ;
                            decFlag = true ;
                        }
                    }
                    else {
                        stack.push( yenCount ) ;
                        indNo ++ ;
                    }
                    if( off < 0 ) {
                        yenLen = (yenCount+off) ;
                        if( yenLen > 0 ) {
                            yenLen = (2<<(yenLen-1))-1 ;
                        }
                        else {
                            yenLen = 0 ;
                        }
                    }
                    else {
                        yenLen = (2<<(indNo-1))-1 ;
                    }
                    for( int j = 0 ; j < yenLen ; j ++ ) {
                        buf.append( "\\" ) ;
                    }
                    buf.append( cote ) ;
                    yenCount = 0 ;
                    if( decFlag ) {
                        indNo -- ;
                    }
                }
                else if( c == '\\' ) {
                    yenCount ++ ;
                }
                else {
                    if( yenCount > 0 ) {
                        for( int j = 0 ; j < yenCount ; j ++ ) {
                            buf.append( "\\" ) ;
                        }
                        yenCount = 0 ;
                    }
                    buf.append( c ) ;
                }
            }
            return buf.toString() ;
        }
        return string ;
    }
    
    /**
     * 指定文字内のダブルコーテーションインデントを1つ上げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String upIndentDoubleCote( String string ) {
        return indentCote( string,0,true ) ;
    }
    
    /**
     * 指定文字内のシングルコーテーションインデントを1つ上げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String upIndentSingleCote( String string ) {
        return indentCote( string,0,false ) ;
    }
    
    /**
     * 指定文字内のダブルコーテーションインデントを1つ下げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String downIndentDoubleCote( String string ) {
        // 文字列で検出されるダブルコーテーションが￥始まりの場合は、処理する.
        boolean exec = false ;
        int len = string.length() ;
        char c,b ;
        b = 0 ;
        for( int i = 0 ; i < len ; i ++ ) {
            c = string.charAt( i ) ;
            if( c == '\"' ) {
                if( b == '\\' ) {
                    exec = true ;
                }
                break ;
            }
            b = c ;
        }
        if( exec ) {
            return indentCote( string,-1,true ) ;
        }
        return string ;
    }
    
    /**
     * 指定文字内のシングルコーテーションインデントを1つ下げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String downIndentSingleCote( String string ) {
        // 文字列で検出されるシングルコーテーションが￥始まりの場合は、処理する.
        boolean exec = false ;
        int len = string.length() ;
        char c,b ;
        b = 0 ;
        for( int i = 0 ; i < len ; i ++ ) {
            c = string.charAt( i ) ;
            if( c == '\'' ) {
                if( b == '\\' ) {
                    exec = true ;
                }
                break ;
            }
            b = c ;
        }
        if( exec ) {
            return indentCote( string,-1,false ) ;
        }
        return string ;
    }
    
    /**
     * ファイル名の存在チェック.
     * @param name 対象のファイル名を設定します.
     * @return boolean [true]の場合、ファイルは存在します.
     */
    public static final boolean isFile( final String name ) {
        File file = new File(name);
        return (file.exists() == true) ? ((file.isDirectory() == false) ? file
                .canRead()
                : false)
                : false;
    }
    
    /**
     * ファイル名のフルパスを取得.
     * @param name 対象のファイル名を設定します.
     * @return String フルパス名が返却されます.
     * @exception Exception 例外.
     */
    public static final String getFullPath( final String name )
        throws Exception {
        String s = ( new File( name ) ).getCanonicalPath();
        if( s.indexOf( "\\" ) != -1 ) {
            s = StringUtils.changeString( s,"\\","/" ) ;
        }
        if( !s.startsWith( "/" ) ) {
            s = "/" + s ;
        }
        return s ;
    }
    
    /**
     * ファイル内容を取得.
     * @param name 対象のファイル名を設定します.
     * @return byte[] バイナリ情報が返却されます.
     * @exception Exception 例外.
     */
    public static final byte[] getFile(String name) throws Exception {
        int len;
        InputStream buf = null;
        ByteArrayOutputStream bo = null;
        byte[] b = new byte[ 1024 ] ;
        try {
            bo = new ByteArrayOutputStream() ;
            buf = new BufferedInputStream( new FileInputStream( name ) ) ;
            while( true ) {
                if( ( len = buf.read(b) ) <= 0 ) {
                    if( len <= -1 ) {
                        break ;
                    }
                    continue ;
                }
                bo.write( b,0,len ) ;
            }
            buf.close() ;
            buf = null ;
        } finally {
            if( buf != null ) {
                try {
                    buf.close();
                } catch (Exception t) {
                }
            }
            buf = null;
        }
        return bo.toByteArray() ;
    }
    
    /**
     * ファイル出力.
     * @param name 対象のファイル名を設定します.
     * @param value 対象のバイナリ情報を設定します.
     * @exception Exception 例外.
     */
    public static final void setFile( String name,byte[] value ) throws Exception {
        OutputStream out = new BufferedOutputStream( new FileOutputStream( name ) ) ;
        try {
            out.write( value ) ;
            out.flush() ;
            out.close() ;
            out = null ;
        } finally {
            if( out != null ) {
                try {
                    out.close() ;
                } catch( Exception e ) {
                }
            }
        }
    }
    
    /**
     * ビットサイズを取得.
     * @param x 対象の長さを設定します.
     * @return int ビットサイズが返却されます.
     */
    public static final int bitMask( int x ) {
        if( x <= 0 ) {
            return 0 ;
        }
        x |= ( x >>  1 );
        x |= ( x >>  2 );
        x |= ( x >>  4 );
        x |= ( x >>  8 );
        x |= ( x >> 16 );
        x = (x & 0x55555555) + (x >> 1 & 0x55555555);
        x = (x & 0x33333333) + (x >> 2 & 0x33333333);
        x = (x & 0x0f0f0f0f) + (x >> 4 & 0x0f0f0f0f);
        x = (x & 0x00ff00ff) + (x >> 8 & 0x00ff00ff);
        x = (x & 0x0000ffff) + (x >>16 & 0x0000ffff);
        return 1 << ( ( (x & 0x0000ffff) + (x >>16 & 0x0000ffff) ) - 1 ) ;
    }
    
    /** 連続左ゼロビット長を取得. **/
    protected static final int nlzs( int x ) {
        x |= ( x >>  1 );
        x |= ( x >>  2 );
        x |= ( x >>  4 );
        x |= ( x >>  8 );
        x |= ( x >> 16 );
        x = (x & 0x55555555) + (x >> 1 & 0x55555555);
        x = (x & 0x33333333) + (x >> 2 & 0x33333333);
        x = (x & 0x0f0f0f0f) + (x >> 4 & 0x0f0f0f0f);
        x = (x & 0x00ff00ff) + (x >> 8 & 0x00ff00ff);
        return (x & 0x0000ffff) + (x >>16 & 0x0000ffff);
    }
    
    /**
     * スタックトレースを文字取得.
     * @param t 対象の例外を設定します.
     * @return String スタックトレースが返却されます.
     */
    public static final String getStackTrace( Throwable t ) {
        StringWriter sw = new StringWriter() ;
        PrintWriter pw = new PrintWriter( sw ) ;
        t.printStackTrace( pw ) ;
        return sw.toString() ;
    }
    
}

