    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 org.opengion.fukurou.util.Closer ;
    019import org.opengion.fukurou.util.LogWriter;
    020
    021import java.sql.Connection;
    022import java.sql.Date;
    023import java.sql.Timestamp;
    024import java.sql.ResultSet;
    025import java.sql.ResultSetMetaData;
    026import java.sql.SQLException;
    027import java.sql.Statement;
    028import java.sql.Types;
    029
    030import java.util.List;
    031import java.util.ArrayList;
    032
    033import org.jfree.data.jdbc.JDBCCategoryDataset;
    034import org.jfree.data.Range;
    035
    036/**
    037 * HybsJDBCCategoryDataset は、org.jfree.data.jdbc.JDBCCategoryDataset を継承したサブクラスで、
    038 * executeQuery(Connection , String )  をオーバーライドしています。
    039 * これは、元のソースのデータベース検索結果を内部で持っておき、getValue(int row, int column)
    040 * メソッドで直接値を返します。
    041 * series の横持ち(標準と同じ) 対応です。
    042 * 参考:JFreeChart : a free chart library for the Java(tm) platform(jfreechart-1.0.6)
    043 *
    044 * @og.rev 3.8.9.2 (2007/07/28) 新規作成
    045 *
    046 * @version  0.9.0  2001/05/05
    047 * @author   Kazuhiko Hasegawa
    048 * @since    JDK1.1,
    049 */
    050public class HybsJDBCCategoryDataset2 extends JDBCCategoryDataset {
    051        private static final long serialVersionUID = 562120130308L ;
    052
    053        private Number[][] numdata = null;
    054        private Range      range   = null;
    055        private final int hsCode = Long.valueOf( System.nanoTime() ).hashCode() ;       // 5.1.9.0 (2010/08/01) equals,hashCode
    056
    057        // 4.3.5.0 (2009/02/01) 各オブジェクトを特定するためのユニークな番号
    058//      private final int uniqNo  = Double.valueOf( Math.random() ).hashCode() ;        // 5.1.8.0 (2010/07/01) 廃止
    059
    060        /**
    061         * Creates a new dataset with the given database connection, and executes
    062         * the supplied query to populate the dataset.
    063         *
    064         * @param connection  the connection.
    065         * @param query  the query.
    066         *
    067         * @throws SQLException if there is a problem executing the query.
    068         */
    069        public HybsJDBCCategoryDataset2( final Connection connection, final String query ) throws SQLException {
    070                super( connection );
    071                innerQuery( connection,query );
    072        }
    073
    074        /**
    075         * Populates the dataset by executing the supplied query against the
    076         * existing database connection.  If no connection exists then no action
    077         * is taken.
    078         *
    079         * The results from the query are extracted and cached locally, thus
    080         * applying an upper limit on how many rows can be retrieved successfully.
    081         *
    082         * @og.rev 4.0.0.0 (2007/11/28) new Long(long) ⇒ Long.valueOf(long) 変更
    083         * @og.rev 4.0.0.0 (2007/11/28) resultSet,statement を Closer でclose する。
    084         * @og.rev 4.0.0.0 (2007/11/28) Range 求めで nullポインタを参照外しの修正
    085         * @og.rev 4.0.0.0 (2007/11/30) public な executeQuery メソッドを private 化します。
    086         *
    087         * @param con  the connection.
    088         * @param query  the query.
    089         *
    090         * @throws SQLException if there is a problem executing the query.
    091         */
    092        @Override
    093        public void executeQuery( final Connection con, final String query ) throws SQLException {
    094                innerQuery( con,query );
    095        }
    096
    097        /**
    098         * Populates the dataset by executing the supplied query against the
    099         * existing database connection.  If no connection exists then no action
    100         * is taken.
    101         *
    102         * The results from the query are extracted and cached locally, thus
    103         * applying an upper limit on how many rows can be retrieved successfully.
    104         *
    105         * @og.rev 4.0.0.0 (2007/11/28) new Long(long) ⇒ Long.valueOf(long) 変更
    106         * @og.rev 4.0.0.0 (2007/11/28) resultSet,statement を Closer でclose する。
    107         * @og.rev 4.0.0.0 (2007/11/28) Range 求めで nullポインタを参照外しの修正
    108         * @og.rev 5.6.2.1 (2013/03/08) Types.DATE と Types.TIMESTAMP で処理を分けます。
    109         *
    110         * @param con  the connection.
    111         * @param query  the query.
    112         *
    113         * @throws SQLException if there is a problem executing the query.
    114         */
    115        private void innerQuery( final Connection con, final String query ) throws SQLException {
    116
    117                Statement statement = null;
    118                ResultSet resultSet = null;
    119                try {
    120                        statement = con.createStatement();
    121                        resultSet = statement.executeQuery(query);
    122                        ResultSetMetaData metaData = resultSet.getMetaData();
    123
    124                        // Range を予め求めておきます。
    125                        double minimum = Double.POSITIVE_INFINITY;
    126                        double maximum = Double.NEGATIVE_INFINITY;
    127
    128                        int columnCount = metaData.getColumnCount();
    129                        if(columnCount < 2) {
    130                                String errMsg = "JDBCCategoryDataset.executeQuery() : insufficient columns "
    131                                                        + "returned from the database. \n"
    132                                                        + " SQL=" + query ;
    133                                throw new SQLException( errMsg );
    134                        }
    135
    136                        List<Number[]> rowList = new ArrayList<Number[]>();
    137                        while (resultSet.next()) {
    138                                Number[] clmList = new Number[columnCount-1];
    139                                // first column contains the row key...
    140                //              Comparable rowKey = resultSet.getString(1);
    141                                String rowKey = resultSet.getString(1);                 // 4.3.3.6 (2008/11/15) Generics警告対応
    142                                for( int column=2; column<=columnCount; column++ ) {
    143
    144                                //      Comparable columnKey = metaData.getColumnName(column);
    145                                        String columnKey = metaData.getColumnName(column);              // 4.3.3.6 (2008/11/15) Generics警告対応
    146                                        int columnType = metaData.getColumnType(column);
    147
    148                                        Number value = null;
    149                                        switch (columnType) {
    150                                                case Types.TINYINT:
    151                                                case Types.SMALLINT:
    152                                                case Types.INTEGER:
    153                                                case Types.BIGINT:
    154                                                case Types.FLOAT:
    155                                                case Types.DOUBLE:
    156                                                case Types.DECIMAL:
    157                                                case Types.NUMERIC:
    158                                                case Types.REAL: {
    159                                                        value = (Number)resultSet.getObject(column);
    160                                                        break;
    161                                                }
    162                                                case Types.DATE:
    163                                                case Types.TIME:  {
    164                                                        Date date = (Date) resultSet.getObject(column);
    165                                                        value = Long.valueOf(date.getTime());
    166                                                        break;
    167                                                }
    168                                                // 5.6.2.1 (2013/03/08) Types.DATE と Types.TIMESTAMP で処理を分けます。
    169                                                case Types.TIMESTAMP: {
    170                                                        Timestamp time = (Timestamp) resultSet.getObject(column);
    171                                                        value = Long.valueOf(time.getTime());
    172                                                        break;
    173                                                }
    174                                                case Types.CHAR:
    175                                                case Types.VARCHAR:
    176                                                case Types.LONGVARCHAR: {
    177                                                        String string = (String)resultSet.getObject(column);
    178                                                        try {
    179                                                                value = Double.valueOf(string);
    180                                                        }
    181                                                        catch (NumberFormatException ex) {
    182                                                                LogWriter.log( ex );
    183                                                                // suppress (value defaults to null)
    184                                                        }
    185                                                        break;
    186                                                }
    187                                                default:
    188                                                        // not a value, can't use it (defaults to null)
    189                                                        break;
    190                                        }
    191                                        clmList[column-2] = value;
    192                                        setValue(value, columnKey, rowKey);
    193
    194                                        // Range 求め
    195                                        if( value != null ) {   // 4.0.0.0 (2007/11/28)
    196                                                double dbl = value.doubleValue();
    197                                                if( dbl     < minimum ) { minimum = dbl; }
    198                                                if( maximum < dbl     ) { maximum = dbl; }
    199                                        }
    200                                }
    201                                rowList.add( clmList );
    202                        }
    203                        numdata = rowList.toArray( new Number[columnCount-1][rowList.size()] );
    204
    205                        range = new Range( minimum, maximum );
    206                }
    207                finally {
    208                        Closer.resultClose( resultSet );
    209                        Closer.stmtClose( statement );
    210                }
    211        }
    212
    213        /**
    214         * 指定された行列から、数字オブジェクトを取得します。
    215         *
    216         * @param       row     行番号
    217         * @param       column  カラム番号(列番号)
    218         *
    219         * @return      指定の行列の値
    220         */
    221        @Override
    222        public Number getValue( final int row, final int column ) {
    223                // 注意：行列の順序が逆です。
    224                return numdata[column][row];
    225        }
    226
    227        /**
    228         * レンジオブジェクトを取得します。(独自メソッド)
    229         *
    230         * @return      レンジオブジェクト
    231         */
    232        public Range getRange() {
    233                return range;
    234        }
    235
    236        /**
    237         * この文字列と指定されたオブジェクトを比較します。
    238         *
    239         * 親クラスで、equals メソッドが実装されているため、警告がでます。
    240         *
    241         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    242         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    243         *
    244         * @param       object  比較するオブジェクト
    245         *
    246         * @return      Objectが等しい場合は true、そうでない場合は false
    247         */
    248        @Override
    249        public boolean equals( final Object object ) {
    250//              return super.equals( object );
    251                if( super.equals( object ) ) {
    252                        return hsCode == ((HybsJDBCCategoryDataset2)object).hsCode;
    253                }
    254                return false;
    255        }
    256
    257        /**
    258         * このオブジェクトのハッシュコードを取得します。
    259         *
    260         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
    261         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
    262         *
    263         * @return      ハッシュコード
    264         */
    265//      public int hashCode() { return super.hashCode() ; }
    266        @Override
    267        public int hashCode() { return hsCode ; }
    268
    269        /**
    270         * このオブジェクトと指定されたオブジェクトを比較します。
    271         *
    272         * @og.rev 4.0.0.0 (2007/11/28) 新規追加
    273         * @og.rev 4.3.5.0 (2009/02/01) 同一オブジェクトかどうかの判定方法変更
    274         * @og.rev 5.1.8.0 (2010/07/01) 廃止
    275         *
    276         * @param anObject Object 比較されるオブジェクト
    277         *
    278         * @return      指定されたオブジェクトが等しい場合は true、そうでない場合は false
    279         */
    280//      public boolean equals( final Object anObject ) {
    281//      //      if( anObject instanceof HybsJDBCCategoryDataset2 ) {
    282//      //              HybsJDBCCategoryDataset2 other = (HybsJDBCCategoryDataset2)anObject ;
    283//      //              return ( uniqNo == other.uniqNo ) ;
    284//      //      }
    285//      //      return false ;
    286//
    287//              if( super.equals( anObject ) ) {
    288//                      HybsJDBCCategoryDataset2 other = ((HybsJDBCCategoryDataset2)anObject);
    289//                      if( numdata != null && numdata == other.numdata &&
    290//                              range   != null && range.equals( other.range ) ) {
    291//                              return true;
    292//                      }
    293//              }
    294//              return false;
    295//      }
    296
    297        /**
    298         * このオブジェクトのハッシュコードを返します。
    299         *
    300         * @og.rev 4.0.0.0 (2007/11/28) 新規追加
    301         * @og.rev 4.3.5.0 (2009/02/01) ハッシュの求め方を変更
    302         * @og.rev 5.1.8.0 (2010/07/01) 廃止
    303         *
    304         * @return      このオブジェクトのハッシュコード値
    305         */
    306//      public int hashCode() {
    307//      //      return uniqNo;
    308//              return super.hashCode() +
    309//                              ((numdata == null) ? 0 : numdata.hashCode()) +
    310//                              ((range   == null) ? 0 : range.hashCode()) ;
    311//      }
    312}