    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.io;
    017
    018import static org.opengion.fukurou.system.HybsConst.CR ;                        // 6.1.0.0 (2014/12/26)
    019import static org.opengion.fukurou.system.HybsConst.DB_FETCH_SIZE;      // 6.9.4.1 (2018/04/09)
    020import org.opengion.fukurou.system.LogWriter;
    021import org.opengion.fukurou.util.ColorMap;                      // 6.0.2.2 (2014/10/03)
    022import org.opengion.fukurou.db.ResultSetValue;          // 6.0.4.0 (2014/11/28)
    023import org.opengion.hayabusa.db.DBTableModel;
    024// import org.opengion.hayabusa.common.HybsSystem;              // 6.9.3.0 (2018/03/26)6.9.3.0 (2018/03/26)
    025
    026import java.sql.Connection;
    027import java.sql.ResultSet;
    028import java.sql.SQLException;
    029import java.sql.Statement;
    030
    031import java.util.List;
    032import java.util.ArrayList;
    033import java.util.Arrays;
    034import java.util.Set;
    035import java.util.HashSet;
    036
    037import java.awt.Color;                                                          // 6.0.2.2 (2014/10/03)
    038
    039import org.jfree.data.Range;
    040import org.jfree.data.category.DefaultCategoryDataset;
    041
    042/**
    043 * HybsCategoryDataset は、org.jfree.data.category.DefaultCategoryDataset を継承したサブクラスで、
    044 * HybsDataset インターフェースの実装クラスになっています。
    045 * これは、JDBCCategoryDatasetの データベース機能と、DBTableModel から Dataset を作成する機能を
    046 * 兼ね備えています。
    047 * HybsDataset インターフェースは、シリーズのラベル指定、カテゴリカラーバー、パレート図用積上げ
    048 * 計算などの処理を行うための、インターフェースで、それらの処理も、HybsCategoryDataset に実装します。
    049 *
    050 * このクラスでは、検索結果を内部で持っておき、getValue(int row, int column)
    051 * メソッドで直接値を返します。
    052 *
    053 * select category,series1,series2,series3,･･･ from ･･･
    054 * series の横持ち(標準と同じ) 対応です。
    055 * category カラムの値は、カテゴリのラベルになり、series1,2,3 のラベルがシリーズラベル、値が
    056 * seriesの値になります。
    057 *
    058 * カテゴリのカラー名の指定を行う場合、最後のカラムが、カラー名の文字列になります。
    059 * select category,series1,series2,series3,･･･,color from ･･･
    060 * color文字列の検索結果は、Dataset には含まれません。
    061 *
    062 * その場合、color カラムがシリーズとして認識されない様に、ChartDatasetTag で、useCategoryColor="true"
    063 * を指定しておく必要があります。このフラグは、HybsCategoryDataset を使う処理以外では効果が
    064 * ありません（シリーズとして使用されてしまう）のでご注意ください。
    065 * このフラグは、カテゴリカラーバーを使う場合には必要ですが、カテゴリカラーバーと（例えばパレート図）
    066 * を合成する場合に、パレート図側にも useCategoryColor="true" を設定しておけば、同じSQL または、
    067 * DBTableModel を使う事ができるというためのフラグです。
    068 *
    069 * なお、Colorコードは、このクラスで作成しますが、Renderer に与える必要があります。
    070 * 通常のRenderer には、categoryにカラーを指定する機能がありませんので、HybsBarRenderer に
    071 * setCategoryColor( Color[] ) メソッドを用意します。(正確には、HybsDrawItem インターフェース)
    072 * このRenderer で、getItemPaint( int  , int )メソッドをオーバーライドすることで、カテゴリごとの
    073 * 色を返します。
    074 *
    075 * @og.rev 6.0.2.2 (2014/10/03) 新規追加
    076 *
    077 * @version  6.0.2.2 (2014/10/03)
    078 * @author   Kazuhiko Hasegawa
    079 * @since    JDK1.6,
    080 */
    081public class HybsCategoryDataset extends DefaultCategoryDataset implements HybsDataset {
    082        private static final long serialVersionUID = 602220141003L ;
    083
    084//      /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ  {@value} */
    085//      private static final int DB_FETCH_SIZE = HybsSystem.sysInt( "DB_FETCH_SIZE" ) ;
    086
    087        private final Set<String> cateCheck     = new HashSet<>();                      // category の重複チェック
    088        private final int       hsCode  = Long.valueOf( System.nanoTime() ).hashCode() ;        // 5.1.9.0 (2010/08/01) equals,hashCode
    089
    090        private String[]        seriesLabels            ;
    091        private boolean         isColorCategory         ;                                               // 6.0.2.2 (2014/10/03)
    092        private boolean         isParetoData            ;                                               // 6.0.2.2 (2014/10/03)
    093
    094        private Number[][]      numdata                         ;
    095        private Color[]         categoryColor           ;
    096        private Range           range                           = new Range( 0, 0 );    // 6.9.7.0 (2018/05/14) データ０件で、処理を抜けるので、初期値を与えておきます。
    097
    098        /**
    099         * デフォルトコンストラクター
    100         *
    101         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    102         */
    103        public HybsCategoryDataset() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    104
    105        /**
    106         * CategoryDataset を構築するに当たり、初期パラメータを設定します。
    107         *
    108         * @og.rev 6.0.2.2 (2014/10/03) 新規追加
    109         *
    110         * @param lbls  シリーズのラベル名配列
    111         * @param isColCate  カテゴリのカラー名の指定有無(true:使用する)
    112         * @param isPareto   パレート図用のDatasetとして処理するかどうか(true:処理する)
    113         */
    114        public void initParam( final String[] lbls , final boolean isColCate , final boolean isPareto ) {
    115                // 6.0.2.5 (2014/10/31) refactoring
    116                if( lbls != null ) { seriesLabels = lbls.clone(); }
    117                isColorCategory = isColCate;
    118                isParetoData    = isPareto;
    119        }
    120
    121        /**
    122         * コネクションと、SQL文字列から、CategoryDataset のデータを作成します。
    123         * 元となる処理は、org.jfree.data.jdbc.JDBCCategoryDataset#executeQuery( Connection,String ) です。
    124         *
    125         * このメソッドでは、先に #initParam(String[],boolean,isPareto) のパラメータを使用して
    126         * 検索した結果のデータを加工、処理します。
    127         * また、内部的に、データをキャッシュする事と、データ範囲を示す レンジオブジェクト を作成します。
    128         *
    129         * @og.rev 6.0.2.2 (2014/10/03) 新規追加
    130         * @og.rev 6.0.2.3 (2014/10/19) パレート図は、100分率にする。
    131         * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    132         * @og.rev 6.4.2.1 (2016/02/05) try-with-resources 文で記述。
    133         * @og.rev 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズを設定。
    134         * @og.rev 6.9.7.0 (2018/05/14) データ０件のときは、処理を中断します。
    135         *
    136         * @param conn  コネクション
    137         * @param query  SQL文字列
    138         *
    139         * @throws SQLException データベースアクセス時のエラー
    140         * @see         org.jfree.data.jdbc.JDBCCategoryDataset#executeQuery( Connection,String )
    141         * @see         org.opengion.fukurou.db.ResultSetValue
    142         */
    143        public void execute( final Connection conn, final String query ) throws SQLException {
    144
    145                // Range を予め求めておきます。
    146                double minimum = Double.POSITIVE_INFINITY;
    147                double maximum = Double.NEGATIVE_INFINITY;
    148                double sum     = 0.0d;                                  // 6.0.2.3 (2014/10/19) パレート図用合計
    149
    150                List<Color> colorList = null;                   // 6.0.2.2 (2014/10/03) カテゴリカラー
    151
    152                // 6.4.2.1 (2016/02/05) try-with-resources 文
    153                try( Statement statement = conn.createStatement();
    154                         ResultSet resultSet = statement.executeQuery(query) ) {
    155
    156                        statement.setFetchSize( DB_FETCH_SIZE );                                // 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ
    157
    158                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    159                        final ResultSetValue rsv = new ResultSetValue( resultSet );
    160
    161                        int dataSize = rsv.getColumnCount() -1;                 // series の個数は、category 分を引いた数。
    162                        if( isColorCategory ) {                                                 // ColorCategory使用時
    163                                colorList       = new ArrayList<>();                    // カテゴリカラー
    164                                dataSize--;                                                                     // 最終カラムが Colorコードなので、マイナスする。
    165                        }
    166
    167                        if( dataSize<1 ) {
    168                                final String errMsg = "JDBCCategoryDataset.executeQuery() : insufficient columns "
    169                                                        + "returned from the database. \n"
    170                                                        + " SQL=" + query ;
    171                                throw new SQLException( errMsg );
    172                        }
    173
    174                        // 6.0.2.0 (2014/09/19) シリーズのラベル名配列を使うときは、シリーズ数必要。
    175                        if( seriesLabels != null && seriesLabels.length < dataSize ) {
    176                                final String errMsg = "seriesLabels を使用する場合は、必ずシリーズ数以上指定してください。"
    177                                                                + CR
    178                                                                + " seriesLabels=" + Arrays.toString( seriesLabels )
    179                                                                + CR
    180                                                                + " seriesLabels.length=" + seriesLabels.length
    181                                                                + " dataSize=" + dataSize
    182                                                                + CR ;
    183                                throw new IllegalArgumentException( errMsg );
    184                        }
    185
    186                        String[] series  = new String[dataSize];
    187                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    188                        final String[] names   = rsv.getNames();
    189                        // ORACLEの引数は、配列＋１から始まるので、metaDataはi＋2から取得。series と、seriesLabels は０から始まる。
    190                        for( int i=0; i<dataSize; i++ ) {
    191                                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    192                                series[i] = seriesLabels == null || seriesLabels[i] == null
    193                                                                        ? names[i+1]
    194                                                                        : seriesLabels[i] ;
    195                        }
    196
    197                        final List<Number[]> rowList = new ArrayList<>();
    198                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    199                        while( rsv.next() ) {
    200                                Number[] clmList = new Number[dataSize];
    201                                // first column contains the row key...
    202                                // 6.0.2.0 (2014/09/19) columnKeyは、series , rowKey は、category に変更する。
    203                                final String category = uniqCategory( resultSet.getString(1) ); // 6.0.2.3 (2014/10/10) categoryの重複回避
    204
    205                                for( int i=0; i<dataSize; i++ ) {                                       // 6.0.2.2 (2014/10/03) dataSize 分回す。
    206                                        Number value = null;
    207                                        // 6.0.2.1 (2014/09/26) org.opengion.fukurou.db.DBUtil に、移動
    208                                        try {
    209                                                // JDBCのアドレス指定は、+2 する。(category 分と、ｱﾄﾞﾚｽが１から始まる為。)
    210                                                // ResultSetValueのカラム番号は、+1 する。(category 分があるため)
    211                                                value = rsv.getNumber( i+1 );
    212                                        }
    213                                        // 7.2.9.5 (2020/11/28) PMD:'catch' branch identical to 'SQLException' branch
    214                                        catch( final SQLException | RuntimeException ex ) {             // 6.0.4.0 (2014/11/28) ResultSetValue を使用するので。
    215                                                LogWriter.log( ex );
    216                                        }
    217//                                      catch( final SQLException ex ) {                // 6.0.4.0 (2014/11/28) ResultSetValue を使用するので。
    218//                                              LogWriter.log( ex );
    219//                                      }
    220//                                      catch( final RuntimeException ex ) {
    221//                                              LogWriter.log( ex );
    222//                                      }
    223
    224                                        clmList[i] = value;
    225                                        addValue(value, series[i], category);           // 6.0.2.0 (2014/09/19) columnKeyは、series , rowKey は、category に変更する。
    226                                        // Range 求め
    227                                        if( value != null ) {
    228                                                final double dbl = value.doubleValue();
    229                                                if( isParetoData ) {                                    // 6.0.2.3 (2014/10/19) パレート図用合計
    230                                                        sum += dbl ;
    231                                                } else {
    232                                                        if( dbl     < minimum ) { minimum = dbl; }
    233                                                        if( maximum < dbl     ) { maximum = dbl; }
    234                                                }
    235                                        }
    236                                }
    237                                rowList.add( clmList );
    238                                // 6.0.2.2 (2014/10/03) ColorCategory は、最後のカラム
    239                                if( isColorCategory ) {
    240                                        // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更。
    241                                        final String colStr = rsv.getValue(dataSize+1);                         // 最後のカラム
    242                                        final Color color   = ColorMap.getColorInstance( colStr );      // 6.0.2.1 (2014/09/26) StringUtil → ColorMap
    243                                        colorList.add( color );
    244                                }
    245                        }
    246                        // 6.9.7.0 (2018/05/14) データ０件のときは、処理を中断します。
    247                        if( rowList.isEmpty() ) { return; }
    248
    249                        numdata = rowList.toArray( new Number[dataSize][rowList.size()] );
    250                }
    251
    252                // colorList が null でないかどうかで判定。
    253                if( isColorCategory && colorList != null ) {
    254                        categoryColor = colorList.toArray( new Color[colorList.size()] );
    255                }
    256
    257                // 6.0.2.3 (2014/10/19) パレート図は、100分率にする。
    258                if( isParetoData ) {
    259                        changeParetoData( sum );
    260                        minimum = 0.0;
    261                        maximum = 100.0;
    262                }
    263
    264                range = new Range( minimum, maximum );
    265        }
    266
    267        /**
    268         * DBTableModelオブジェクトから、CategoryDataset のデータを作成します。
    269         * openGionの独自処理メソッドです。
    270         *
    271         * このメソッドでは、先に #initParam(String[],boolean,isPareto) のパラメータを使用して
    272         * 検索した結果のデータを加工、処理します。
    273         * また、内部的に、データをキャッシュする事と、データ範囲を示す レンジオブジェクト を作成します。
    274         *
    275         * @og.rev 6.0.2.2 (2014/10/03) 新規追加
    276         * @og.rev 6.0.2.3 (2014/10/19) パレート図は、100分率にする。
    277         * @og.rev 6.9.7.0 (2018/05/14) データ０件のときは、処理を中断します。
    278         *
    279         * @param table DBTableModelオブジェクト
    280         * @see         #execute( Connection,String )
    281         */
    282        public void execute( final DBTableModel table ) {
    283
    284                // 6.9.7.0 (2018/05/14) データ０件のときは、処理を中断します。
    285                if( table == null || table.getRowCount() == 0 ) { return; }
    286
    287                final int clmNo = table.getColumnCount();
    288                final int rowNo = table.getRowCount();
    289
    290                // Range を予め求めておきます。
    291                double minimum = Double.POSITIVE_INFINITY;
    292                double maximum = Double.NEGATIVE_INFINITY;
    293                double sum     = 0.0d;                                  // 6.0.2.3 (2014/10/19) パレート図用合計
    294
    295                int dataSize = clmNo -1;                                                // series の個数は、category 分を引いた数。
    296                List<Color> colorList = null;                   // 6.0.2.2 (2014/10/03) カテゴリカラー
    297                if( isColorCategory ) {                                                 // ColorCategory使用時
    298                        colorList       = new ArrayList<>();            // カテゴリカラー
    299                        dataSize--;                                                                     // 最終カラムが Colorコードなので、マイナスする。
    300                }
    301
    302                numdata = new Number[rowNo][clmNo];
    303
    304                // ※ DBTableModel の row,col と、Dataset の row,col は、逆になっています。
    305                for( int row=0; row<rowNo; row++ ) {
    306                        final String   category = uniqCategory( table.getValue( row,0 ) );      // 6.0.2.3 (2014/10/10) categoryの重複回避
    307                        final String[] vals     = table.getValues( row );
    308                        for( int clm=0; clm<dataSize; clm++ ) {
    309                                final String sval = vals[clm+1];                                // ２番目(アドレス=1)からカラムデータを取得
    310                                final double val  = sval == null || sval.isEmpty() ? 0.0d : Double.parseDouble( sval ) ;                // 6.4.2.1 (2016/02/05) PMD refactoring. Useless parentheses.
    311
    312                                addValue( val , seriesLabels[clm] , category );         // val,row,clm
    313                                numdata[row][clm] = Double.valueOf( val );                      // 6.0.2.4 (2014/10/17) 効率の悪いメソッド
    314                                // Range 求め
    315                                if( isParetoData ) {                                    // 6.0.2.3 (2014/10/19) パレート図用合計
    316                                        sum += val ;
    317                                } else {
    318                                        if( val     < minimum ) { minimum = val; }
    319                                        if( maximum < val     ) { maximum = val; }
    320                                }
    321                        }
    322
    323                        // 6.0.2.2 (2014/10/03) ColorCategory は、最後のカラム
    324                        if( isColorCategory ) {
    325                                final String colStr = vals[dataSize+1];                 // 最後のカラム
    326                                final Color color   = ColorMap.getColorInstance( colStr );      // 6.0.2.1 (2014/09/26) StringUtil → ColorMap
    327                                colorList.add( color );
    328                        }
    329                }
    330
    331                // colorList が null でないかどうかで判定。
    332                if( isColorCategory && colorList != null ) {
    333                        categoryColor = colorList.toArray( new Color[colorList.size()] );
    334                }
    335
    336                // 6.0.2.3 (2014/10/19) パレート図は、100分率にする。
    337                if( isParetoData ) {
    338                        changeParetoData( sum );
    339                        minimum = 0.0;
    340                        maximum = 100.0;
    341                }
    342
    343                range = new Range( minimum, maximum );
    344        }
    345
    346        /**
    347         * 指定された行列から、数字オブジェクトを取得します。
    348         *
    349         * @param       row     行番号(シリーズ：横持=clm相当)
    350         * @param       column  カラム番号(カテゴリ：縦持ち=row相当)
    351         *
    352         * @return      指定の行列の値
    353         */
    354        @Override
    355        public Number getValue( final int row, final int column ) {
    356                // 注意：行列の順序が逆です。
    357                return numdata[column][row];
    358        }
    359
    360        /**
    361         * レンジオブジェクトを取得します。(独自メソッド)
    362         *
    363         * @return      レンジオブジェクト
    364         */
    365        public Range getRange() {
    366                return range;
    367        }
    368
    369        /**
    370         * パレート図用のDatasetに値を書き換えます。(独自メソッド)
    371         *
    372         * 色々と方法はあると思いますが、簡易的に、内部の Number配列を
    373         * 積上げ計算して、パレート図用のデータを作成します。
    374         * レンジオブジェクト も変更します。
    375         *
    376         * ※ 注意：親クラスの内部に持っている実データは変更されていないので、
    377         * 場合によっては、おかしな動きをするかもしれません。
    378         * その場合は、上位にもデータをセットするように変更する必要があります。
    379         *
    380         * なお、行列の順序が、イメージと異なりますので、注意願います。
    381         * (columnは、series , row は、category で、シリーズを積み上げます)
    382         *
    383         * @og.rev 6.0.2.1 (2014/09/26) 新規追加
    384         * @og.rev 6.0.2.2 (2014/10/03) HybsDataset i/f
    385         * @og.rev 6.0.2.3 (2014/10/19) パレート図は、100分率にする。
    386         *
    387         * @param  sum データの合計
    388         */
    389        private void changeParetoData( final double sum ) {
    390                if( numdata == null || numdata.length == 0 || numdata[0].length == 0 || sum == 0.0 ) { return ; }
    391
    392                final int rowCnt = numdata[0].length ;
    393                final int clmCnt = numdata.length ;
    394
    395                for( int rowNo=0; rowNo<rowCnt; rowNo++ ) {                     // 行列が逆。
    396                        double val = 0.0;               // 初期値
    397                        for( int clmNo=0; clmNo<clmCnt; clmNo++ ) {             // 積上げ計算するカラムでループを回す。
    398                                final Number v1Num = numdata[clmNo][rowNo];
    399                                if( v1Num != null ) {
    400                                        val += v1Num.doubleValue();                             // 積上げ計算は、元の値のままにしておきます。
    401                                }
    402                                // データをセットするときに、１００分率にします。
    403                                numdata[clmNo][rowNo] = Double.valueOf( Math.round( val * 1000.0 / sum ) / 10.0 );
    404        // きちんと計算するなら、BigDecimal で、スケールを指定して四捨五入すべき・・・かも
    405        //                      java.math.BigDecimal bd = new BigDecimal( val * 100.0 / sum );
    406        //                      numdata[clmNo][rowNo] = bd.setScale( 1, java.math.RoundingMode.HALF_UP );
    407                        }
    408                }
    409        }
    410
    411        /**
    412         * categoryカラー配列を取得します。(独自メソッド)
    413         *
    414         * このクラスは、一番最後のカラムを、色文字列として処理し、categoryにColorを指定できます。
    415         * select文で指定されていなかった場合は、null を返します。
    416         *
    417         * select category,series1,series2,series3,･･･,color from ･･･
    418         *
    419         * @og.rev 6.0.2.2 (2014/10/03) 新規追加
    420         *
    421         * なお、Colorコードは、このクラスで作成しますが、Renderer に与える必要があります。
    422         * 通常のRenderer には、categoryにカラーを指定する機能がありませんので、HybsBarRenderer に
    423         * setCategoryColor( Color[] ) メソッドを用意します。(正確には、HybsDrawItem インターフェース)
    424         * このRenderer で、getItemPaint( int  , int )メソッドをオーバーライドすることで、カテゴリごとの
    425         * 色を返します。
    426         * この設定を行うと、シリーズは、カテゴリと同一色になります。
    427         *
    428         * @return      categoryカラー配列(なければ null)
    429         */
    430        public Color[] getCategoryColor() {
    431                // 6.0.2.5 (2014/10/31) refactoring
    432                return ( categoryColor == null ) ? null : categoryColor.clone();
    433        }
    434
    435        /**
    436         * category の重複をさけて、必要であれば、新しいカテゴリ名を作成します。
    437         *
    438         * カテゴリが同じ場合、JFreeChartでは、表示されません。これは、同じカテゴリと認識され
    439         * 値が上書きされるためです。
    440         * この問題は、なかなか気づきにくく、デバッグ等に時間がかかってしまいます。
    441         * 重複チェックを行い、警告してもよいのですが、ここでは、新しいカテゴリ名を作成することで
    442         * エラーを回避しつつ、とりあえずグラフ表示をするようにします。
    443         *
    444         * @og.rev 6.0.2.3 (2014/10/10) 新規追加
    445         *
    446         * @param       category        元のカテゴリ名
    447         * @return      新しい元のカテゴリ名
    448         */
    449        private String uniqCategory( final String category ) {
    450                String newCate = category ;
    451                int i = 0;
    452                while( !cateCheck.add( newCate ) ) {    // すでに存在している場合。
    453                        newCate = category + "(" + (i++ ) + ")" ;
    454                }
    455
    456                return newCate ;
    457        }
    458
    459        /**
    460         * この文字列と指定されたオブジェクトを比較します。
    461         *
    462         * 親クラスで、equals メソッドが実装されているため、警告がでます。
    463         *
    464         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    465         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    466         *
    467         * @param       object  比較するオブジェクト
    468         *
    469         * @return      Objectが等しい場合は true、そうでない場合は false
    470         */
    471        @Override
    472        public boolean equals( final Object object ) {
    473                // 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
    474                return super.equals( object ) && hsCode == ((HybsCategoryDataset)object).hsCode;
    475        }
    476
    477        /**
    478         * このオブジェクトのハッシュコードを取得します。
    479         *
    480         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    481         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    482         *
    483         * @return      ハッシュコード
    484         */
    485        @Override
    486        public int hashCode() { return hsCode ; }
    487}