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}