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.util.ArrayList;
import java.util.List;

import org.maachang.mimdb.core.util.DateFormatUtils;
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() {}
    
    /**
     * 文字列内容が数値かチェック.
     * @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( 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( 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 ) {
            if( isNumeric( (String)o ) ) {
                return _cDate( StringUtils.parseLong((String)o) ) ;
            }
            return DateFormatUtils.getDate( (String)o ) ;
        }
        throw new IOException( "java.sql.Date型変換に失敗しました[" + o + "]" ) ;
    }
    
    /** 時間のみ表現. **/
    private static final java.sql.Time _cTime( 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( 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 ) {
            if( isNumeric( (String)o ) ) {
                return _cTime( StringUtils.parseLong((String)o) ) ;
            }
            return DateFormatUtils.getTime( (String)o ) ;
        }
        throw new IOException( "java.sql.Time型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 日付時間変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.sql.Timestamp convertSqlTimestamp( 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 ) {
            if( isNumeric( (String)o ) ) {
                return new java.sql.Timestamp( StringUtils.parseLong((String)o) ) ;
            }
            return DateFormatUtils.getTimestamp( (String)o ) ;
        }
        throw new IOException( "java.sql.Timestamp型変換に失敗しました[" + o + "]" ) ;
    }
    
    /**
     * 通常日付変換.
     * @param n 変換対象の条件を設定します.
     * @return 変換された内容が返却されます.
     * @exception Exception 例外.
     */
    public static final java.util.Date convertDate( 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 ) {
            if( isNumeric( (String)o ) ) {
                return new java.sql.Timestamp( StringUtils.parseLong((String)o) ) ;
            }
            return DateFormatUtils.getTimestamp( (String)o ) ;
        }
        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( final String string,final int off,final 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 ;
            final 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( final String string ) {
        return indentCote( string,0,true ) ;
    }
    
    /**
     * 指定文字内のシングルコーテーションインデントを1つ上げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String upIndentSingleCote( final String string ) {
        return indentCote( string,0,false ) ;
    }
    
    /**
     * 指定文字内のダブルコーテーションインデントを1つ下げる.
     * @param string 対象の文字列を設定します.
     * @return String 変換された文字列が返されます.
     */
    public static final String downIndentDoubleCote( final String string ) {
        // 文字列で検出されるダブルコーテーションが￥始まりの場合は、処理する.
        boolean exec = false ;
        final 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( final String string ) {
        // 文字列で検出されるシングルコーテーションが￥始まりの場合は、処理する.
        boolean exec = false ;
        final 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( String name ) {
        File file = new File(name);
        return ( file.exists() && !file.isDirectory() ) ;
    }
    
    /**
     * ディレクトリ名の存在チェック.
     * @param name 対象のディレクトリ名を設定します.
     * @return boolean [true]の場合、ディレクトリは存在します.
     */
    public static final boolean isDir( String name ) {
        File file = new File(name);
        return ( file.exists() && file.isDirectory() ) ;
    }
    
    /**
     * 指定情報が読み込み可能かチェック.
     * @param name 対象のファイル／ディレクトリ名を設定します.
     * @return boolean [true]の場合、読み込み可能です.
     */
    public static final boolean isRead( String name ) {
        File file = new File(name);
        return ( file.exists() && file.canRead() ) ;
    }
    
    /**
     * 指定情報が書き込み可能かチェック.
     * @param name 対象のファイル／ディレクトリ名を設定します.
     * @return boolean [true]の場合、書き込み可能です.
     */
    public static final boolean isWrite( String name ) {
        File file = new File(name);
        return ( file.exists() && file.canWrite() ) ;
    }
    
    /**
     * 指定情報が読み書き込み可能かチェック.
     * @param name 対象のファイル／ディレクトリ名を設定します.
     * @return boolean [true]の場合、読み書き込み可能です.
     */
    public static final boolean isIO( String name ) {
        File file = new File(name);
        return ( file.exists() && file.canWrite() && file.canWrite() ) ;
    }
    
    /**
     * ファイル名のフルパスを取得.
     * @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( final 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( final String name,final 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( final Throwable t ) {
        StringWriter sw = new StringWriter() ;
        PrintWriter pw = new PrintWriter( sw ) ;
        t.printStackTrace( pw ) ;
        return sw.toString() ;
    }
    
}

