    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 *  １．select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
    038 * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
    039 *  ２．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         *  １．select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
    062         * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
    063         *  ２．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         *  １．select series,x(時間),y(値) from XX order by series,x(時間) の縦持ちで、series のキーブレイク処理
    083         * TimeSeriesLineH、TimeSeriesBarH、StackedTimeSeriesLineH の場合は横持です。
    084         *  ２．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         *  １．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         *  ２．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         *  １．８文字以上ある場合、yyyyMMdd 部分を切り出して、年月日情報を作成します。
    223         *  ２．１４文字以上ある場合、残りの、hhmmss 部分を切り出して、時分秒情報を作成します。
    224         *  ３．それ以外の場合は、"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}