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        /**
058         * Creates a new dataset with the given database connection, and executes
059         * the supplied query to populate the dataset.
060         *
061         * @param connection  the connection.
062         * @param query  the query.
063         *
064         * @throws SQLException if there is a problem executing the query.
065         */
066        public HybsJDBCCategoryDataset2( final Connection connection, final String query ) throws SQLException {
067                super( connection );
068                innerQuery( connection,query );
069        }
070
071        /**
072         * Populates the dataset by executing the supplied query against the
073         * existing database connection.  If no connection exists then no action
074         * is taken.
075         *
076         * The results from the query are extracted and cached locally, thus
077         * applying an upper limit on how many rows can be retrieved successfully.
078         *
079         * @og.rev 4.0.0.0 (2007/11/28) new Long(long) ⇒ Long.valueOf(long) 変更
080         * @og.rev 4.0.0.0 (2007/11/28) resultSet,statement を Closer でclose する。
081         * @og.rev 4.0.0.0 (2007/11/28) Range 求めで nullポインタを参照外しの修正
082         * @og.rev 4.0.0.0 (2007/11/30) public な executeQuery メソッドを private 化します。
083         *
084         * @param con  the connection.
085         * @param query  the query.
086         *
087         * @throws SQLException if there is a problem executing the query.
088         */
089        @Override
090        public void executeQuery( final Connection con, final String query ) throws SQLException {
091                innerQuery( con,query );
092        }
093
094        /**
095         * Populates the dataset by executing the supplied query against the
096         * existing database connection.  If no connection exists then no action
097         * is taken.
098         *
099         * The results from the query are extracted and cached locally, thus
100         * applying an upper limit on how many rows can be retrieved successfully.
101         *
102         * @og.rev 4.0.0.0 (2007/11/28) new Long(long) ⇒ Long.valueOf(long) 変更
103         * @og.rev 4.0.0.0 (2007/11/28) resultSet,statement を Closer でclose する。
104         * @og.rev 4.0.0.0 (2007/11/28) Range 求めで nullポインタを参照外しの修正
105         * @og.rev 5.6.2.1 (2013/03/08) Types.DATE と Types.TIMESTAMP で処理を分けます。
106         *
107         * @param con  the connection.
108         * @param query  the query.
109         *
110         * @throws SQLException if there is a problem executing the query.
111         */
112        private void innerQuery( final Connection con, final String query ) throws SQLException {
113
114                Statement statement = null;
115                ResultSet resultSet = null;
116                try {
117                        statement = con.createStatement();
118                        resultSet = statement.executeQuery(query);
119                        ResultSetMetaData metaData = resultSet.getMetaData();
120
121                        // Range を予め求めておきます。
122                        double minimum = Double.POSITIVE_INFINITY;
123                        double maximum = Double.NEGATIVE_INFINITY;
124
125                        int columnCount = metaData.getColumnCount();
126                        if(columnCount < 2) {
127                                String errMsg = "JDBCCategoryDataset.executeQuery() : insufficient columns "
128                                                        + "returned from the database. \n"
129                                                        + " SQL=" + query ;
130                                throw new SQLException( errMsg );
131                        }
132
133                        List<Number[]> rowList = new ArrayList<Number[]>();
134                        while (resultSet.next()) {
135                                Number[] clmList = new Number[columnCount-1];
136                                // first column contains the row key...
137                //              Comparable rowKey = resultSet.getString(1);
138                                String rowKey = resultSet.getString(1);                 // 4.3.3.6 (2008/11/15) Generics警告対応
139                                for( int column=2; column<=columnCount; column++ ) {
140
141                                //      Comparable columnKey = metaData.getColumnName(column);
142                                        String columnKey = metaData.getColumnName(column);              // 4.3.3.6 (2008/11/15) Generics警告対応
143                                        int columnType = metaData.getColumnType(column);
144
145                                        Number value = null;
146                                        switch (columnType) {
147                                                case Types.TINYINT:
148                                                case Types.SMALLINT:
149                                                case Types.INTEGER:
150                                                case Types.BIGINT:
151                                                case Types.FLOAT:
152                                                case Types.DOUBLE:
153                                                case Types.DECIMAL:
154                                                case Types.NUMERIC:
155                                                case Types.REAL: {
156                                                        value = (Number)resultSet.getObject(column);
157                                                        break;
158                                                }
159                                                case Types.DATE:
160                                                case Types.TIME:  {
161                                                        Date date = (Date) resultSet.getObject(column);
162                                                        value = Long.valueOf(date.getTime());
163                                                        break;
164                                                }
165                                                // 5.6.2.1 (2013/03/08) Types.DATE と Types.TIMESTAMP で処理を分けます。
166                                                case Types.TIMESTAMP: {
167                                                        Timestamp time = (Timestamp) resultSet.getObject(column);
168                                                        value = Long.valueOf(time.getTime());
169                                                        break;
170                                                }
171                                                case Types.CHAR:
172                                                case Types.VARCHAR:
173                                                case Types.LONGVARCHAR: {
174                                                        String string = (String)resultSet.getObject(column);
175                                                        try {
176                                                                value = Double.valueOf(string);
177                                                        }
178                                                        catch (NumberFormatException ex) {
179                                                                LogWriter.log( ex );
180                                                                // suppress (value defaults to null)
181                                                        }
182                                                        break;
183                                                }
184                                                default:
185                                                        // not a value, can't use it (defaults to null)
186                                                        break;
187                                        }
188                                        clmList[column-2] = value;
189                                        setValue(value, columnKey, rowKey);
190
191                                        // Range 求め
192                                        if( value != null ) {   // 4.0.0.0 (2007/11/28)
193                                                double dbl = value.doubleValue();
194                                                if( dbl     < minimum ) { minimum = dbl; }
195                                                if( maximum < dbl     ) { maximum = dbl; }
196                                        }
197                                }
198                                rowList.add( clmList );
199                        }
200                        numdata = rowList.toArray( new Number[columnCount-1][rowList.size()] );
201
202                        range = new Range( minimum, maximum );
203                }
204                finally {
205                        Closer.resultClose( resultSet );
206                        Closer.stmtClose( statement );
207                }
208        }
209
210        /**
211         * 指定された行列から、数字オブジェクトを取得します。
212         *
213         * @param       row     行番号
214         * @param       column  カラム番号(列番号)
215         *
216         * @return      指定の行列の値
217         */
218        @Override
219        public Number getValue( final int row, final int column ) {
220                // 注意:行列の順序が逆です。
221                return numdata[column][row];
222        }
223
224        /**
225         * レンジオブジェクトを取得します。(独自メソッド)
226         *
227         * @return      レンジオブジェクト
228         */
229        public Range getRange() {
230                return range;
231        }
232
233        /**
234         * この文字列と指定されたオブジェクトを比較します。
235         *
236         * 親クラスで、equals メソッドが実装されているため、警告がでます。
237         *
238         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
239         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
240         *
241         * @param       object  比較するオブジェクト
242         *
243         * @return      Objectが等しい場合は true、そうでない場合は false
244         */
245        @Override
246        public boolean equals( final Object object ) {
247                if( super.equals( object ) ) {
248                        return hsCode == ((HybsJDBCCategoryDataset2)object).hsCode;
249                }
250                return false;
251        }
252
253        /**
254         * このオブジェクトのハッシュコードを取得します。
255         *
256         * @og.rev 5.1.8.0 (2010/07/01) findbug対応
257         * @og.rev 5.1.9.0 (2010/08/01) findbug対応
258         *
259         * @return      ハッシュコード
260         */
261        @Override
262        public int hashCode() { return hsCode ; }
263}