    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.hayabusa.resource;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.hayabusa.common.HybsSystemException;
    020import org.opengion.fukurou.util.StringUtil;
    021import static org.opengion.fukurou.system.HybsConst.CR ;                                // 6.1.0.0 (2014/12/26)
    022import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    023
    024import java.util.Hashtable;
    025import java.util.List;
    026import java.util.ArrayList;
    027import java.util.Arrays;
    028import java.util.Comparator ;
    029import java.io.Serializable;
    030
    031import javax.naming.Context;
    032import javax.naming.NamingEnumeration;
    033import javax.naming.NamingException;
    034import javax.naming.directory.DirContext;
    035import javax.naming.directory.InitialDirContext;
    036import javax.naming.directory.SearchControls;
    037import javax.naming.directory.SearchResult;
    038import javax.naming.directory.Attribute;
    039import javax.naming.directory.Attributes;
    040
    041/**
    042 * LDAPの内容を検索するための、ldapQueryタグです。
    043 *
    044 * 検索した結果は、配列で取得します。
    045 *
    046 * 下記の項目については、src/resource/システムパラメータ に、予め
    047 * 設定しておくことで、タグごとに指定する必要がなくなります。
    048 * ・LDAP_INITIAL_CONTEXT_FACTORY
    049 * ・LDAP_PROVIDER_URL
    050 * ・LDAP_ENTRYDN
    051 * ・LDAP_PASSWORD
    052 * ・LDAP_SEARCH_BASE
    053 * ・LDAP_SEARCH_SCOPE
    054 * ・LDAP_SEARCH_REFERRAL
    055 *
    056 * @og.rev 3.7.1.0 (2005/04/15) ＬＤＡＰにアクセスできる、LDAPSearch.java を新規に作成。
    057 * @og.group その他入力
    058 *
    059 * @version  4.0
    060 * @author       Kazuhiko Hasegawa
    061 * @since    JDK5.0,
    062 */
    063public class LDAPSearch {
    064
    065        private String                  initctx                         = HybsSystem.sys( "LDAP_INITIAL_CONTEXT_FACTORY" );
    066        private String                  providerURL             = HybsSystem.sys( "LDAP_PROVIDER_URL" );
    067        private String                  entrydn                         = HybsSystem.sys( "LDAP_ENTRYDN" );
    068        private String                  password                        = HybsSystem.sys( "LDAP_PASSWORD" );            // 4.2.2.0 (2008/05/10)
    069        private String                  searchbase                      = HybsSystem.sys( "LDAP_SEARCH_BASE" );
    070        private String                  referral                        = HybsSystem.sys( "LDAP_SEARCH_REFERRAL" ); // 5.6.7.0 (201/07/27)
    071
    072        // 検索範囲。OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 1 つ
    073        private String                  searchScope                     = HybsSystem.sys( "LDAP_SEARCH_SCOPE" );
    074        private static final long       COUNTLIMIT              = 0;            // 返すエントリの最大数。0 の場合、フィルタを満たすエントリをすべて返す
    075        private int                             timeLimit                       ;                       // 結果が返されるまでのミリ秒数。0 の場合、無制限
    076        private String[]                attrs                           ;                       // エントリと一緒に返される属性の識別子。null の場合、すべての属性を返す。空の場合、属性を返さない
    077        private boolean                 returningObjFlag        ;                       // true の場合、エントリの名前にバインドされたオブジェクトを返す。false 場合、オブジェクトを返さない
    078        private boolean                 derefLinkFlag           ;                       // true の場合、検索中にリンクを間接参照する
    079
    080        private int                             executeCount            ;                       // 検索/実行件数
    081        private int                     maxRowCount                     ;                       // 最大検索数(0は無制限)
    082        private SearchControls  constraints                     ;
    083        private DirContext              ctx                                     ;
    084        private String[]                orderBy                         ;                       // ｿｰﾄ項目(csv)
    085        private boolean[]               desc                            ;                       // 降順ﾌﾗｸﾞ
    086
    087        /**
    088         * デフォルトコンストラクター
    089         *
    090         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    091         */
    092        public LDAPSearch() { super(); }                // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    093
    094        /**
    095         * LDAPパラメータを利用して、LDAP検索用オブジェクトを構築します。
    096         *
    097         * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応
    098         * @og.rev 5.6.7.0 (2013/07/27) LDAPのREFERRAL対応
    099         *
    100         * 通常、パラメータをセット後、search( String filter ) の実行前に、呼びます。
    101         */
    102        public void init() {
    103                final Hashtable<String,String> env = new Hashtable<>();
    104                env.put(Context.INITIAL_CONTEXT_FACTORY, initctx);
    105                env.put(Context.PROVIDER_URL, providerURL);
    106                if( ! StringUtil.isNull( referral ) ) { // 5.6.7.0 (2013/07/27)
    107                        env.put( Context.REFERRAL, referral );
    108                }
    109                // 3.7.1.1 (2005/05/31)
    110                if( ! StringUtil.isNull( password ) ) {
    111                        env.put( Context.SECURITY_CREDENTIALS, password.trim() );
    112                }
    113                // 4.2.2.0 (2008/05/10) entrydn 属性の追加
    114                if( ! StringUtil.isNull( entrydn ) ) {
    115                        env.put( Context.SECURITY_PRINCIPAL  , entrydn );
    116                }
    117
    118                try {
    119                        ctx = new InitialDirContext(env);
    120                        constraints = new SearchControls(
    121                                                                        changeScopeString( searchScope ),
    122                                                                        COUNTLIMIT                      ,
    123                                                                        timeLimit                       ,
    124                                                                        attrs                           ,
    125                                                                        returningObjFlag        ,
    126                                                                        derefLinkFlag
    127                                                                                );
    128                } catch( final NamingException ex ) {
    129                        final String errMsg = "LDAP検索用オブジェクトの初期化に失敗しました。" ;
    130                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    131                }
    132        }
    133
    134        /**
    135         * LDPA から、値を取り出し、List オブジェクトを作成します。
    136         * 引数の headerAdd をtrueにする事により、１件目に、キー情報の配列を返します。
    137         *
    138         * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応
    139         *
    140         * @param       filter  フィルター文字列
    141         *
    142         * @return      検索結果の Listオブジェクト
    143         */
    144        public List<String[]> search( final String filter ) {
    145
    146                final List<String[]> list = new ArrayList<>();
    147                try {
    148                        final NamingEnumeration<SearchResult> results = ctx.search(searchbase, filter, constraints);    // 4.3.3.6 (2008/11/15) Generics警告対応
    149
    150                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );   // 6.1.0.0 (2014/12/26) refactoring
    151                        while( results != null && results.hasMore() ) {
    152                                if( maxRowCount > 0 && maxRowCount <= executeCount ) { break ; }
    153                                final SearchResult si = results.next();         // 4.3.3.6 (2008/11/15) Generics警告対応
    154                                final Attributes at = si.getAttributes();
    155                                // attrs が null の場合は、キー情報を取得します。
    156                                if( attrs == null ) {
    157                                        final NamingEnumeration<String> ne = at.getIDs();       // 4.3.3.6 (2008/11/15) Generics警告対応
    158                                        final List<String> lst = new ArrayList<>();
    159                                        while( ne.hasMore() ) {
    160                                         lst.add( ne.next() );  // 4.3.3.6 (2008/11/15) Generics警告対応
    161                                        }
    162                                        ne.close();
    163                                        attrs = lst.toArray( new String[lst.size()] );
    164                                }
    165
    166                                String[] values = new String[attrs.length];
    167                                boolean flag = false;           // 属性チェックフラグ
    168                                for( int i=0; i<attrs.length; i++ ) {
    169                                        if( maxRowCount > 0 && maxRowCount <= executeCount ) { break ; }
    170                                        final Attribute attr = at.get(attrs[i]);
    171                                        if( attr != null ) {
    172                                                final NamingEnumeration<?> vals = attr.getAll();                        // 4.3.3.6 (2008/11/15) Generics警告対応
    173                                                buf.setLength(0);                                                                                       // 6.1.0.0 (2014/12/26) refactoring
    174                                                if( vals.hasMore() ) { getDataChange( vals.next(),buf ) ;}      // 4.2.2.0 (2008/05/10)
    175                                                while( vals.hasMore() ) {
    176                                                        buf.append( ',' ) ;                                             // 6.0.2.5 (2014/10/31) char を append する。
    177                                                        getDataChange( vals.next(),buf ) ;              // 4.2.2.0 (2008/05/10)
    178                                                }
    179                                                values[i] = buf.toString();
    180                                                flag = true;
    181                                        }
    182                                }
    183                                if( flag ) {
    184                                        list.add( values );
    185                                        executeCount++ ;
    186                                }
    187                        }
    188                        if( results != null ) { results.close(); }
    189                } catch( final NamingException ex ) {
    190                        final String errMsg = "List オブジェクトの検索に失敗しました。"
    191                                                + CR
    192                                                + "searchbase や、entrydn の記述をご確認ください。"
    193                                                + CR
    194                                                + "searchbase:" + searchbase
    195                                                + " , entrydn:" + entrydn ;
    196                        throw new HybsSystemException( errMsg,ex );             // 3.5.5.4 (2004/04/15) 引数の並び順変更
    197                }
    198                return sort( list,attrs ) ;
    199        }
    200
    201        /**
    202         * LDAPから取得したデータの変換を行います。
    203         *
    204         * 主に、バイト配列(byte[]) オブジェクトの場合、文字列に戻します。
    205         *
    206         * @og.rev 4.2.2.0 (2008/05/10) 新規追加
    207         *
    208         * @param       obj     主にバイト配列データ
    209         * @param       buf     元のStringBuilder
    210         *
    211         * @return      データを追加したStringBuilder
    212         */
    213        private StringBuilder getDataChange( final Object obj, final StringBuilder buf ) {
    214                // 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
    215                if( obj != null ) {
    216                        if( obj instanceof byte[] ) {
    217                                final byte[] bb = (byte[])obj ;
    218                                char[] chs = new char[bb.length];
    219                                for( int i=0; i<bb.length; i++ ) {
    220                                        chs[i] = (char)bb[i];
    221                                }
    222                                buf.append( chs );
    223                        }
    224                        else {
    225                                buf.append( obj ) ;
    226                        }
    227                }
    228                return buf;
    229        }
    230
    231        /**
    232         * 検索範囲(OBJECT/ONELEVEL/SUBTREE)を設定します(初期値:LDAP_SEARCH_SCOPE)。
    233         *
    234         * 検索範囲を OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか 1 つです。
    235         * 指定文字列は、それぞれ『OBJECT』『ONELEVEL』『SUBTREE』です。
    236         *
    237         * @param       scope   SearchControlsの検索範囲
    238         */
    239        public void setSearchScope( final String scope ) {
    240                searchScope = StringUtil.nval( scope, searchScope );
    241                if( ! "OBJECT".equals( searchScope ) &&
    242                        ! "ONELEVEL".equals( searchScope ) &&
    243                        ! "SUBTREE".equals( searchScope ) ) {
    244                                final String errMsg = "検索範囲は、『OBJECT』『ONELEVEL』『SUBTREE』の中から選択して下さい。"
    245                                                                + "[" + searchScope + "]" ;
    246                                throw new HybsSystemException( errMsg );
    247                }
    248        }
    249
    250        /**
    251         * 引数の searchScope 文字列(『OBJECT』『ONELEVEL』『SUBTREE』のどれか)を、
    252         * SearchControls クラス定数である、OBJECT_SCOPE、ONELEVEL_SCOPE、SUBTREE_SCOPE のどれか
    253         *  1 つに設定します。
    254         *
    255         * @param       scope   searchScope文字列
    256         *
    257         * @return      SearchControls定数
    258         */
    259        private int changeScopeString( final String scope ) {
    260                final int rtnScope;
    261                if( "OBJECT".equals( scope ) )        { rtnScope = SearchControls.OBJECT_SCOPE ; }
    262                else if( "ONELEVEL".equals( scope ) ) { rtnScope = SearchControls.ONELEVEL_SCOPE ; }
    263                else if( "SUBTREE".equals( scope ) )  { rtnScope = SearchControls.SUBTREE_SCOPE ; }
    264                else {
    265                        final String errMsg = "Search Scope in 『OBJECT』『ONELEVEL』『SUBTREE』Selected"
    266                                                        + "[" + searchScope + "]" ;
    267                        throw new HybsSystemException( errMsg );
    268                }
    269                return rtnScope ;
    270        }
    271
    272        /**
    273         * これらの SearchControls の時間制限をミリ秒単位で設定します(初期値:0[無制限])。
    274         *
    275         * 値が 0 の場合、無制限に待つことを意味します。
    276         *
    277         * @param       limit   ミリ秒単位の時間制限(初期値:無制限)
    278         */
    279        public void setTimeLimit( final int limit ) {
    280                timeLimit = limit;
    281        }
    282
    283        /**
    284         * 検索中のリンクへの間接参照を有効または無効[true/false]にします(初期値:false)。
    285         *
    286         * 検索中のリンクへの間接参照を有効または無効にします。
    287         *
    288         * @param       deref   リンクを逆参照する場合は true、そうでない場合は false(初期値:false)
    289         */
    290        public void setDerefLinkFlag( final boolean deref ) {
    291                derefLinkFlag = deref;
    292        }
    293
    294        /**
    295         * 結果の一部としてオブジェクトを返すことを有効または無効[true/false]にします(初期値:false)。
    296         *
    297         * 無効にした場合、オブジェクトの名前およびクラスだけが返されます。
    298         * 有効にした場合、オブジェクトが返されます。
    299         *
    300         * @param       pbjflag オブジェクトが返される場合は true、そうでない場合は false(初期値:false)
    301         */
    302        public void setReturningObjFlag( final boolean pbjflag ) {
    303                returningObjFlag = pbjflag;
    304        }
    305
    306        /**
    307         * レジストリの最大検索件数をセットします(初期値:0[無制限])。
    308         *
    309         * DBTableModelのデータとして登録する最大件数をこの値に設定します。
    310         * サーバーのメモリ資源と応答時間の確保の為です。
    311         * 0 は、無制限です。(初期値は、無制限です。)
    312         *
    313         * @param       count   レジストリの最大検索件数
    314         */
    315        public void setMaxRowCount( final int count ) {
    316                maxRowCount = count;
    317        }
    318
    319        /**
    320         * 検索の一部として返される属性を文字列配列でセットします。
    321         *
    322         * null は属性が何も返されないことを示します。
    323         * このメソッドからは、空の配列をセットすることは出来ません。
    324         *
    325         * @param       atr     返される属性を識別する属性 ID の配列(可変長引数)
    326         */
    327        public void setAttributes( final String... atr ) {
    328                if( atr != null && atr.length > 0 ) {           // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    329                        attrs = new String[atr.length];
    330                        System.arraycopy( atr,0,attrs,0,atr.length );
    331                }
    332        }
    333
    334        /**
    335         * 検索の一部として返される属性を文字列配列で取得します。
    336         *
    337         * setAttributes で、設定した文字列配列が返されます。
    338         * 属性配列に、 null をセットした場合、全属性が返されます。
    339         *
    340         * @return      返される属性を識別する属性 ID の配列
    341         * @og.rtnNotNull
    342         */
    343        public String[] getAttributes() {
    344                return (attrs == null) ? new String[0] : attrs.clone() ;
    345        }
    346
    347        /**
    348         * 初期コンテキストファクトリを指定します(初期値:システムパラメータ の INITIAL_CONTEXT_FACTORY)。
    349         *
    350         * 初期値は、システムパラメータ の INITIAL_CONTEXT_FACTORY 属性です。
    351         * 例)com.sun.jndi.ldap.LdapCtxFactory
    352         *
    353         * @param       ctx INITIAL_CONTEXT_FACTORY属性
    354         */
    355        public void setInitctx( final String ctx ) {
    356                initctx = StringUtil.nval( ctx, initctx );
    357        }
    358
    359        /**
    360         * サービスプロバイダの構成情報を指定します(初期値:システムパラメータ の LDAP_PROVIDER_URL)。
    361         *
    362         * プロトコルとサーバーとポートを指定します。
    363         * 例)『ldap://ldap.opengion.org:389』
    364         *
    365         * @param       url PROVIDER_URL属性
    366         */
    367        public void setProviderURL( final String url ) {
    368                providerURL = StringUtil.nval( url, providerURL );
    369        }
    370
    371        /**
    372         * 検索するコンテキストまたはオブジェクトの名前を設定します(初期値:システムパラメータ の LDAP_SEARCH_BASE)。
    373         *
    374         * 例)『soOUID=employeeuser,o=opengion,c=JP』
    375         *
    376         * @param       base SEARCHBASE属性
    377         */
    378        public void setSearchbase( final String base ) {
    379                searchbase = StringUtil.nval( base, searchbase );
    380        }
    381
    382        /**
    383         * 属性の取得元のオブジェクトの名前を設定します(初期値:システムパラメータ の LDAP_ENTRYDN)。
    384         *
    385         * 例)『cn=inquiry-sys,o=opengion,c=JP』
    386         *
    387         * @param       dn 取得元のオブジェクトの名前
    388         */
    389        public void setEntrydn( final String dn ) {
    390                entrydn = StringUtil.nval( dn, entrydn );
    391        }
    392
    393        /**
    394         * 属性の取得元のオブジェクトのパスワードを設定します(初期値:システムパラメータ の LDAP_PASSWORD)。
    395         *
    396         * @og.rev 4.2.2.0 (2008/05/10) LDAP パスワード取得対応
    397         *
    398         * @param       pwd 取得元のオブジェクトのパスワード
    399         */
    400        public void setPassword( final String pwd ) {
    401                password = StringUtil.nval( pwd, password );
    402        }
    403
    404        /**
    405         * 検索した結果を表示する表示順をファイル属性名で指定します。
    406         *
    407         * attributes 属性で指定するキー、または、LDAPから返されたキーについて
    408         * その属性でソートします。逆順を行う場合は、DESC を指定のカラム名の後ろに
    409         * 付けて下さい。
    410         *
    411         * @param       ordr    ソートキーを指定。
    412         */
    413        public void setOrderBy( final String ordr ) {
    414                orderBy = StringUtil.csv2Array( ordr );
    415
    416                desc = new boolean[orderBy.length];
    417                for( int i=0; i<orderBy.length; i++ ) {
    418                        String key = orderBy[i].trim();
    419                        final int ad = key.indexOf( " DESC" ) ;
    420                        if( ad > 0 ) {
    421                                desc[i] = true;
    422                                key = key.substring( 0,ad );
    423                        }
    424                        else {
    425                                desc[i] = false;
    426                        }
    427                        orderBy[i] = key ;
    428                }
    429        }
    430
    431        /**
    432         * リストオブジェクトをヘッダーキーに対応させてソートします。
    433         *
    434         * @og.rev 4.2.2.0 (2008/05/10) ソート条件を増やします。
    435         *
    436         * @param       inLst   ソートするリストオブジェクト(文字列配列のList)
    437         * @param       headers ソートするキーになる文字列配列(可変長引数)
    438         *
    439         * @return      ソート結果のリストオブジェクト
    440         */
    441        private List<String[]> sort( final List<String[]> inLst,final String... headers ) {
    442                // 4.2.2.0 (2008/05/10) ソート条件を増やします。
    443                if( orderBy == null || orderBy.length == 0 ||
    444                        headers == null || headers.length == 0 ||               // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
    445                        inLst.isEmpty()                                                         ) { return inLst; }
    446
    447                int[] no = new int[orderBy.length];
    448                for( int i=0; i<orderBy.length; i++ ) {
    449                        final String key = orderBy[i] ;
    450                        no[i] = -1;     // 未存在時のマーカー
    451                        for( int j=0; j<headers.length; j++ ) {
    452                                if( key.equalsIgnoreCase( headers[j] ) ) {
    453                                        no[i] = j ;     break;
    454                                }
    455                        }
    456                        if( no[i] < 0 ) {
    457                                final String errMsg = "指定の Order BY キーは、ヘッダー列に存在しません。"
    458                                                        + "order Key=[" + key + "] , attri=["
    459                                                        + StringUtil.array2csv( headers ) + "]" + CR ;
    460                                throw new HybsSystemException( errMsg );
    461                        }
    462                }
    463
    464                final String[][] data = inLst.toArray( new String[inLst.size()][inLst.get(0).length] );         // 6.3.9.0 (2015/11/06) This statement may have some unnecessary parentheses(PMD)
    465                Arrays.sort( data, new IdComparator( no,desc ) );
    466                return Arrays.asList( data );                           // 6.2.0.0 (2015/02/27)
    467        }
    468
    469        /**
    470         * LDAPの検索結果を並び替える為の Comparator実装内部クラスです。
    471         *
    472         * @og.group その他入力
    473         *
    474         * @version  4.0
    475         * @author       Kazuhiko Hasegawa
    476         * @since    JDK5.0,
    477         */
    478        private static final class IdComparator implements Comparator<String[]>,Serializable {
    479                private static final long serialVersionUID = 400020050131L ;    // 4.0.0.0 (2005/01/31)
    480
    481                private final int[]             no ;
    482                private final boolean[] desc ;
    483                private final int               cnt ;
    484
    485                /**
    486                 * コンストラクター
    487                 *
    488                 * @param       no              ソートするリストオブジェクト
    489                 * @param       desc    ソートするキーになる文字列配列
    490                 */
    491                public IdComparator( final int[] no , final boolean[] desc ) {
    492                        this.no         = no;
    493                        this.desc       = desc;
    494                        cnt                     = no.length;
    495                }
    496
    497                /**
    498                 * Comparator インターフェースのcompareメソッド
    499                 *
    500                 * 順序付けのために 2 つの引数を比較します。
    501                 * 最初の引数が 2 番目の引数より小さい場合は負の整数、
    502                 * 両方が等しい場合は 0、最初の引数が 2 番目の引数より
    503                 * 大きい場合は正の整数を返します。
    504                 *
    505                 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。トリッキーな値の置き換えをやめます。
    506                 *
    507                 * @param       s1      比較対象の最初のオブジェクト
    508                 * @param       s2      比較対象の 2 番目のオブジェクト
    509                 * @return      最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数
    510                 */
    511                @Override       // Comparator
    512                public int compare( final String[] s1,final String[] s2 ) {
    513                        if( s1 == null ) { return -1; }
    514
    515                        for( int i=0; i<cnt; i++ ) {
    516                                if( s1[no[i]] == null ) { return -1; }
    517                                if( s2[no[i]] == null ) { return 1; }   // 5.5.2.6 (2012/05/25) 比較を途中で止めないために、nullチェックしておく。
    518                                // 5.5.2.6 (2012/05/25) findbugs対応
    519                                final int rtn = desc[i] ? s2[no[i]].compareTo( s1[no[i]] ) : s1[no[i]].compareTo( s2[no[i]] ) ;
    520                                if( rtn != 0 ) { return rtn ;}
    521                        }
    522                        return 0;
    523                }
    524
    525        //      public boolean equals(Object obj) {
    526        //              return ( this == obj );
    527        //      }
    528        }
    529
    530        /**
    531         * このオブジェクトの文字列表現を返します。
    532         * 基本的にデバッグ目的に使用します。
    533         *
    534         * @return このクラスの文字列表現
    535         * @og.rtnNotNull
    536         */
    537        @Override
    538        public String toString() {
    539                // 6.0.2.5 (2014/10/31) char を append する。
    540                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    541                        .append( "  initctx      [" ).append( initctx      ).append( ']' ).append( CR )
    542                        .append( "  providerURL  [" ).append( providerURL  ).append( ']' ).append( CR )
    543                        .append( "  entrydn      [" ).append( entrydn      ).append( ']' ).append( CR )
    544                        .append( "  searchbase   [" ).append( searchbase   ).append( ']' ).append( CR )
    545                        .append( "  searchScope  [" ).append( searchScope  ).append( ']' ).append( CR )
    546                        .append( "  executeCount [" ).append( executeCount ).append( ']' ).append( CR )
    547                        .append( "  attributes   [" ).append( StringUtil.array2line( attrs,"," ) )
    548                        .append( ']' ).append( CR );
    549
    550                return buf.toString();
    551        }
    552}