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 java.sql.Connection;
019// import java.sql.Date;
020import java.sql.ResultSet;
021import java.sql.ResultSetMetaData;
022import java.sql.SQLException;
023import java.sql.Statement;
024// import java.sql.Types;
025// import java.util.Locale;
026
027import org.opengion.fukurou.util.Closer;
028import org.opengion.fukurou.util.LogWriter;
029
030import org.jfree.data.time.TimeSeriesCollection;
031import org.jfree.data.time.TimeSeries;
032import org.jfree.data.time.RegularTimePeriod;
033// import org.jfree.data.time.Year;                             // Year  (int year) 
034// import org.jfree.data.time.Month;                    // Month (int month, int year) 
035// import org.jfree.data.time.Day;                              // Day   (int day, int month, int year)
036// import org.jfree.data.time.Hour;                             // Hour  (int hour, int day, int month, int year)
037// import org.jfree.data.time.Minute;                   // Minute(int minute, int hour, int day, int month, int year)
038import org.jfree.data.time.Second;                      // Second(int second, int minute, int hour, int day, int month, int year) 
039
040/**
041 * HybsTimeSeriesCollection は、org.jfree.data.time.TimeSeriesCollection を継承したサブクラスで、
042 * オブジェクト作成とともに JDBC接続して、TimeSeries データを作成し、セットします。
043 * TimeSeriesCollection は、XYDataset のサブクラスです。
044 *
045 * TimeSeriesLineV、TimeSeriesBarV、StackedTimeSeriesLineV の場合は縦持です。
046 *  1.select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
047 * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
048 *  2.select x(時間),y1(値),y2(値),・・・ from XX  order by x(時間) の横持
049 * series のキーブレイク処理されます。
050 *
051 * Stacked**** は、各シリーズのy(値)を、次々に加算します。各時間で実績数をセットし、最終時刻に
052 * どれだけ出来上がったかを表示するのに便利です。
053 *
054 * @og.rev 5.6.1.0 (2013/02/01) 新規作成
055 *
056 * @version  0.9.0  2001/05/05
057 * @author   Kazuhiko Hasegawa
058 * @since    JDK1.1,
059 */
060public class HybsTimeSeriesCollection extends TimeSeriesCollection {
061        private static final long serialVersionUID = 561020130201L ;
062
063        private final boolean vhFlag ;          // select文で series を縦持V(true)か横持H(false)かを指定。
064        private final boolean isStacked ;       // データの加算処理を行うかどうか?   true:行う/false:行わない
065
066        /**
067         * チャートタイプを引数にとる、コンストラクター
068         *
069         * TimeSeriesLineV、TimeSeriesBarV、StackedTimeSeriesLineV の場合は縦持です。
070         *  1.select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
071         * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
072         *  2.select x(時間),y1(値),y2(値),・・・ from XX  order by x(時間) の横持
073         * series のキーブレイク処理されます。
074         *
075         * Stacked**** は、各シリーズのy(値)を、次々に加算します。各時間で実績数をセットし、最終時刻に
076         * どれだけ出来上がったかを表示するのに便利です。
077         *
078         * @param type  チャートタイプ
079         */
080        public HybsTimeSeriesCollection( final String type ) {
081                super();
082                vhFlag    = ( type.endsWith( "V" ) ) ;                          // V:縦持 = true / H:横持 = false
083                isStacked = ( type.startsWith( "Stacked" ) ) ;          // Stacked:積み上げ = true
084        }
085
086        /**
087         * HybsTimeSeriesCollection オブジェクトの内部に、DB検索結果のデータを設定します。
088         *
089         * このメソッドは、series の 縦持/横持を、コンストラクターで判定しています。
090         * TimeSeriesLineV、TimeSeriesBarV、StackedTimeSeriesLineV の場合は縦持です。
091         *  1.select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
092         * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
093         *  2.select x(時間),y1(値),y2(値),・・・ from XX  order by x(時間) の横持
094         * series のキーブレイク処理されます。
095         *
096         * @param con  the connection.
097         * @param query  the query.
098         * @throws SQLException データベース実行エラーが発生した場合
099         *
100         */
101        public void executeQuery( final Connection con, final String query ) throws SQLException {
102                if( vhFlag ) { innerQueryV( con,query ); }
103                else             { innerQueryH( con,query ); }
104        }
105
106        /**
107         * HybsTimeSeriesCollection オブジェクトの内部に、DB検索結果のデータを設定します(縦持)。
108         * このメソッドが呼ばれるのは、TimeSeriesLineV、TimeSeriesBarV、StackedTimeSeriesLineV の場合です。
109         *
110         * このメソッドは、series の 縦持を想定しています。
111         *  1.select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
112         * series のキーブレイク処理されます。
113         *
114         * @param con  the connection.
115         * @param query  the query.
116         *
117         */
118        private void innerQueryV( final Connection con, final String query ) throws SQLException {
119
120                Statement statement = null;
121                ResultSet resultSet = null;
122                try {
123                        statement = con.createStatement();
124                        resultSet = statement.executeQuery(query);
125                        ResultSetMetaData metaData = resultSet.getMetaData();
126
127                        int columnCount = metaData.getColumnCount();
128
129                        if(columnCount < 3) {
130                                String errMsg = "HybsTimeSeriesCollection.innerQueryV() : 実行できません。\n"
131                                                        + "select series,x(時間),y(値) は、最低必要です。それ以降は無視します。"
132                                                        + " SQL=" + query ;
133                                throw new SQLException( errMsg );
134                        }
135
136                        String bkSeries = null;         // キーブレイクのための過去のSeries
137                        double bkyn     = 0.0;
138
139                        TimeSeries timeSeries = null;
140                        while (resultSet.next()) {
141                                // first column contains the row key...
142                                String seriVal  = resultSet.getString(1);                                               // 縦持ちの場合は、データの値がシリーズ名になる。
143                                if( seriVal != null && !seriVal.equals( bkSeries ) ) {
144                                        if( timeSeries != null ) { addSeries( timeSeries ); }           // キーブレイクでセット
145                                        timeSeries = new TimeSeries( seriVal );
146                                        bkSeries = seriVal ;
147                                        bkyn     = 0.0;
148                                }
149
150                                String dateVal  = resultSet.getString(2);                       // x(時間)
151                                RegularTimePeriod timep = getTimePeriod( dateVal );
152
153                                double yn = resultSet.getDouble(3);                                     // y(値)
154                                bkyn = ( isStacked ) ? bkyn + yn : yn ;                         // isStacked = true の場合は、加算していく
155
156                                timeSeries.add( timep, bkyn );
157                        }
158                        if( timeSeries != null ) { addSeries( timeSeries ); }           // キーブレイクでセット
159                }
160                finally {
161                        Closer.resultClose( resultSet ) ;
162                        Closer.stmtClose( statement ) ;
163                }
164        }
165
166        /**
167         * HybsTimeSeriesCollection オブジェクトの内部に、DB検索結果のデータを設定します(横持)。
168         * このメソッドが呼ばれるのは、TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合です。
169         *
170         * このメソッドは、series の 横持を想定しています。
171         *  2.select x(時間),y1(値),y2(値),・・・ from XX  order by x(時間) の横持
172         * で、y1, y2 ・・・ が series として処理されます。
173         * series のラベルは、y1, y2 ・・・のカラム名になります。
174         *
175         * @param con  the connection.
176         * @param query  the query.
177         */
178        private void innerQueryH( final Connection con, final String query ) throws SQLException {
179
180                Statement statement = null;
181                ResultSet resultSet = null;
182                try {
183                        statement = con.createStatement();
184                        resultSet = statement.executeQuery(query);
185                        ResultSetMetaData metaData = resultSet.getMetaData();
186
187                        int columnCount = metaData.getColumnCount();
188
189                        if(columnCount < 2) {
190                                String errMsg = "HybsTimeSeriesCollection.innerQueryH() : 実行できません。\n"
191                                                        + "select x(時間),y1(値),y2(値) , ・・・・ は、最低必要です。"
192                                                        + " SQL=" + query ;
193                                throw new SQLException( errMsg );
194                        }
195
196                        // 各シリーズに対するカラムタイプを先に求めておく
197                        int seriSu = columnCount-1;                             // カラム数-1( x(時間) )
198                        TimeSeries[] timeSeries = new TimeSeries[seriSu];
199                        double[]     bkyn       = new double[seriSu];
200                        for( int j=0; j<seriSu; j++ ) {
201                                timeSeries[j] = new TimeSeries( metaData.getColumnLabel(j+2) );         // 横持の場合は、カラム名をシリーズ名にする。
202                                bkyn[j]       = 0.0;
203                        }
204
205                        while (resultSet.next()) {
206                                // first column contains the row key...
207                                String dateVal  = resultSet.getString(1);                       // x(時間)
208                                RegularTimePeriod timep = getTimePeriod( dateVal );
209
210                                for( int j=0; j<seriSu; j++ ) {
211                                        double yn = resultSet.getDouble(j+2);
212                                        bkyn[j] = ( isStacked ) ? bkyn[j] + yn : yn ;   // isStacked = true の場合は、加算していく
213                                        timeSeries[j].add( timep, bkyn[j] );
214                                }
215                        }
216
217                        for( int j=0; j<seriSu; j++ ) {
218                                addSeries( timeSeries[j] );
219                        }
220                }
221                finally {
222                        Closer.resultClose( resultSet ) ;
223                        Closer.stmtClose( statement ) ;
224                }
225        }
226
227        /**
228         * 日付文字列 から、RegularTimePeriodオブジェクト を生成します。
229         *
230         * このメソッドでは、日付文字列 として、"yyyyMMdd" 形式と "yyyyMMddhhmmss" 形式のみ認めています。
231         *  1.8文字以上ある場合、yyyyMMdd 部分を切り出して、年月日情報を作成します。
232         *  2.14文字以上ある場合、残りの、hhmmss 部分を切り出して、時分秒情報を作成します。
233         *  3.それ以外の場合は、"20100101000000" として、処理します。
234         *
235         * @param       dateVal 日付文字列(yyyyMMddhhmmss 形式)
236         *
237         * @return      RegularTimePeriodオブジェクト(Secondオブジェクト)
238         */
239        private RegularTimePeriod getTimePeriod( final String dateVal ) {
240                int second=0, minute=0, hour=0, day=1, month=1, year=2010 ;
241                if( dateVal != null ) {
242                        if( dateVal.length() >= 8 ) {
243                                year  = Integer.parseInt( dateVal.substring( 0,4 ) );
244                                month = Integer.parseInt( dateVal.substring( 4,6 ) );
245                                day   = Integer.parseInt( dateVal.substring( 6,8 ) );
246                        }
247                        if( dateVal.length() >= 14 ) {
248                                hour   = Integer.parseInt( dateVal.substring( 8,10 ) );
249                                minute = Integer.parseInt( dateVal.substring( 10,12 ) );
250                                second = Integer.parseInt( dateVal.substring( 12,14 ) );
251                        }
252                }
253
254                return new Second( second,minute,hour,day,month,year ) ;
255        }
256
257//      private Number getNumber( final Object objVal , final int columnType ) {
258//              Number value = null;
259//
260//              switch (columnType) {
261//                      case Types.TINYINT:
262//                      case Types.SMALLINT:
263//                      case Types.INTEGER:
264//                      case Types.BIGINT:
265//                      case Types.FLOAT:
266//                      case Types.DOUBLE:
267//                      case Types.DECIMAL:
268//                      case Types.NUMERIC:
269//                      case Types.REAL: {
270//                              value = (Number)objVal;
271//                              break;
272//                      }
273//                      case Types.DATE:
274//                      case Types.TIME:
275//                      case Types.TIMESTAMP: {
276//                              Date date = (Date) objVal;
277//                              value = Long.valueOf(date.getTime());
278//                              break;
279//                      }
280//                      case Types.CHAR:
281//                      case Types.VARCHAR:
282//                      case Types.LONGVARCHAR: {
283//                              String string = (String)objVal;
284//                              try {
285//                                      value = Double.valueOf(string);
286//                              }
287//                              catch (NumberFormatException ex) {
288//      //                              LogWriter.log( ex );
289//                                      // suppress (value defaults to null)
290//                              }
291//                              break;
292//                      }
293//                      default:
294//                              // not a value, can't use it (defaults to null)
295//                              break;
296//              }
297//              return value;
298//      }
299}