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