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     */
016    package org.opengion.hayabusa.io;
017    
018    import static org.opengion.fukurou.util.HybsConst.CR ;          // 6.1.0.0 (2014/12/26)
019    import org.opengion.fukurou.util.Closer ;
020    import org.opengion.fukurou.util.LogWriter;
021    import org.opengion.fukurou.util.ColorMap;                      // 6.0.2.2 (2014/10/03)
022    // import org.opengion.fukurou.db.DBUtil;                       // 6.0.4.0 (2014/11/28)
023    import org.opengion.fukurou.db.ResultSetValue;          // 6.0.4.0 (2014/11/28)
024    // import org.opengion.hayabusa.common.HybsSystem;
025    import org.opengion.hayabusa.db.DBTableModel;
026    
027    import java.sql.Connection;
028    import java.sql.ResultSet;
029    // import java.sql.ResultSetMetaData;                           // 6.0.4.0 (2014/11/28)
030    import java.sql.SQLException;
031    import java.sql.Statement;
032    
033    import java.util.List;
034    import java.util.ArrayList;
035    import java.util.Arrays;
036    // import java.util.Locale;
037    import java.util.Set;
038    import java.util.HashSet;
039    
040    import java.awt.Color;                                                          // 6.0.2.2 (2014/10/03)
041    
042    import org.jfree.data.Range;
043    import org.jfree.data.category.DefaultCategoryDataset;
044    
045    /**
046     * HybsCategoryDataset は、org.jfree.data.category.DefaultCategoryDataset を継承したサブクラスで?
047     * HybsDataset インターフェースの実?ラスになって?す?
048     * これは、JDBCCategoryDatasetの ??タベ?ス機?と、DBTableModel から Dataset を作?する機??
049     * 兼ね備えて?す?
050     * HybsDataset インターフェースは、シリーズのラベル??カ?リカラーバ?、パレート図用積上げ
051     * 計算などの処?行うための、インターフェースで、それらの処?、HybsCategoryDataset に実?ます?
052     * 
053     * こ?クラスでは、検索結果を?部で持っておき、getValue(int row, int column)
054     * メソ?で直接値を返します?
055     * 
056     * select category,series1,series2,series3,??? from ???
057     * series の横持ち(標準と同じ) 対応です?
058     * category カラ??値は、カ?リのラベルになり?series1,2,3 のラベルがシリーズラベル、??
059     * seriesの値になります?
060     *
061     * カ?リのカラー名??を行う場合???カラ?、カラー名???になります?
062     * select category,series1,series2,series3,???,color from ???
063     * color??の検索結果は、Dataset には含まれません?
064     *
065     * そ?場合?color カラ?シリーズとして認識されな?に、ChartDatasetTag で、useCategoryColor="true"
066     * を指定しておく?があります?こ?フラグは、HybsCategoryDataset を使???外では効果が
067     * ありません?シリーズとして使用されてしま??でご注意く???
068     * こ?フラグは、カ?リカラーバ?を使??合には?ですが、カ?リカラーバ?と?例えばパレート図??
069     * を合成する?合に、パレート図側に?useCategoryColor="true" を設定しておけば、同じSQL また??
070     * DBTableModel を使?ができると?ためのフラグです?
071     *
072     * なお?Colorコード?、このクラスで作?しますが、Renderer に与える?があります?
073     * 通常のRenderer には、categoryにカラーを指定する機?がありませんので、HybsBarRenderer に
074     * setCategoryColor( Color[] ) メソ?を用意します?(正確には、HybsDrawItem インターフェース)
075     * こ?Renderer で、getItemPaint( int  , int )メソ?をオーバ?ライドすることで、カ?リごとの
076     * 色を返します?
077     *
078     * @og.rev 5.8.5.0 (2015/02/06) 6.0.2.2 (2014/10/03) からの??
079     *
080     * @version  5.8.5.0 (2015/02/06)
081     * @author   Kazuhiko Hasegawa
082     * @since    JDK1.6,
083     */
084    public class HybsCategoryDataset extends DefaultCategoryDataset implements HybsDataset {
085            private static final long serialVersionUID = 602220141003L ;
086    
087            private final Set<String> cateCheck       = new HashSet<String>();          // category の重?ェ?
088            private final int       hsCode  = Long.valueOf( System.nanoTime() ).hashCode() ;        // 5.1.9.0 (2010/08/01) equals,hashCode
089    
090            private String[]        seriesLabels            ;
091            private boolean         isColorCategory         ;                               // 6.0.2.2 (2014/10/03)
092            private boolean         isParetoData            ;                               // 6.0.2.2 (2014/10/03)
093    
094            private Number[][]      numdata                         ;
095            private Color[]         categoryColor           ;
096            private Range           range                           ;
097    
098            /**
099             * CategoryDataset を構築するに当たり?初期パラメータを設定します?
100             *
101             * @og.rev 6.0.2.2 (2014/10/03) 新規追?
102             *
103             * @param lbls  シリーズのラベル名??
104             * @param isColCate  カ?リのカラー名??有無(true:使用する)
105             * @param isPareto   パレート図用のDatasetとして処?るかど?(true:処??
106             */
107            public void initParam( final String[] lbls , final boolean isColCate , final boolean isPareto ) {
108                    // 6.0.2.5 (2014/10/31) refactoring
109    //              seriesLabels    = lbls;
110                    if( lbls != null ) { seriesLabels = lbls.clone(); }
111                    isColorCategory = isColCate;
112                    isParetoData    = isPareto;
113            }
114    
115            /**
116             * コネクションと、SQL??から、CategoryDataset の??タを作?します?
117             * ?なる???、org.jfree.data.jdbc.JDBCCategoryDataset#executeQuery( Connection,String ) です?
118             *
119             * こ?メソ?では、?に #initParam(String[],boolean,isPareto) のパラメータを使用して
120             * 検索した結果の??タを加工、??ます?
121             * また???、データをキャ?ュする事と、データ?を示?レンジオブジェク?を作?します?
122             *
123             * @og.rev 6.0.2.2 (2014/10/03) 新規追?
124             * @og.rev 6.0.2.3 (2014/10/19) パレート図は?00?にする?
125             * @og.rev 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更?
126             *
127             * @param con  コネクション
128             * @param query  SQL??
129             *
130             * @throws SQLException ??タベ?スアクセス時?エラー
131             * @see         org.jfree.data.jdbc.JDBCCategoryDataset#executeQuery( Connection,String )
132             * @see         org.opengion.fukurou.db.ResultSetValue
133             */
134            public void execute( final Connection con, final String query ) throws SQLException {
135    
136                    // Range を予め求めておきます?
137                    double minimum = Double.POSITIVE_INFINITY;
138                    double maximum = Double.NEGATIVE_INFINITY;
139                    double sum     = 0.0d;                                  // 6.0.2.3 (2014/10/19) パレート図用合?
140    
141                    List<Color> colorList = null;                     // 6.0.2.2 (2014/10/03) カ?リカラー
142    
143                    Statement statement = null;
144                    ResultSet resultSet = null;
145                    try {
146                            statement = con.createStatement();
147                            resultSet = statement.executeQuery(query);
148    
149                            // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更?
150    //                      ResultSetMetaData metaData = resultSet.getMetaData();
151                            final ResultSetValue rsv = new ResultSetValue( resultSet );
152    
153    //                      int dataSize = metaData.getColumnCount() -1;    // series の個数は、category ?引いた数?
154                            int dataSize = rsv.getColumnCount() -1;                 // series の個数は、category ?引いた数?
155                            if( isColorCategory ) {                                                 // ColorCategory使用?
156                                    colorList       = new ArrayList<Color>();         // カ?リカラー
157                                    dataSize--;                                                                     // ?カラ? Colorコードなので、?イナスする?
158                            }
159    
160                            if( dataSize<1 ) {
161                                    final String errMsg = "JDBCCategoryDataset.executeQuery() : insufficient columns "
162                                                            + "returned from the database. \n"
163                                                            + " SQL=" + query ;
164                                    throw new SQLException( errMsg );
165                            }
166    
167                            // 6.0.2.0 (2014/09/19) シリーズのラベル名?列を使?き?、シリーズ数??
168                            if( seriesLabels != null && seriesLabels.length < dataSize ) {
169                                    final String errMsg = "seriesLabels を使用する場合?、?シリーズ数以上指定してください?
170                                                                    + CR
171                                                                    + " seriesLabels=" + Arrays.toString( seriesLabels )
172                                                                    + CR
173                                                                    + " seriesLabels.length=" + seriesLabels.length
174                                                                    + " dataSize=" + dataSize
175                                                                    + CR ;
176                                    throw new IllegalArgumentException( errMsg );
177                            }
178    
179                            String[] series  = new String[dataSize];
180                            // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更?
181                            final String[] names   = rsv.getNames();
182    //                      int[] columnType = new int[dataSize];
183                            // ORACLEの引数は??列+1から始まる?で、metaDataはi??から取得?series と、seriesLabels は?から始まる?
184                            for( int i=0; i<dataSize; i++ ) {
185                                    series[i] = seriesLabels != null && seriesLabels[i] != null
186                                                                            ? seriesLabels[i]
187    //                                                                      : metaData.getColumnLabel(i+2).toUpperCase( Locale.JAPAN );
188                                                                            : names[i+1] ;
189    //                              columnType[i] = metaData.getColumnType(i+2);
190                            }
191    
192                            final List<Number[]> rowList = new ArrayList<Number[]>();
193                            // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更?
194    //                      while (resultSet.next()) {
195                            while (rsv.next()) {
196                                    Number[] clmList = new Number[dataSize];
197                                    // first column contains the row key...
198                                    // 6.0.2.0 (2014/09/19) columnKeyは、series , rowKey は、category に変更する?
199    //                              String category = resultSet.getString(1);                                       // 4.3.3.6 (2008/11/15) Generics警告対?
200                                    final String category = uniqCategory( resultSet.getString(1) ); // 6.0.2.3 (2014/10/10) categoryの重?避
201    
202                                    for( int i=0; i<dataSize; i++ ) {                                    // 6.0.2.2 (2014/10/03) dataSize ?す?
203                                            Number value = null;
204                                            // 6.0.2.1 (2014/09/26) org.opengion.fukurou.db.DBUtil に、移?
205                                            try {
206                                                    // JDBCのアドレス???2 する?category ????レスが1から始まる為?
207    //                                              value = DBUtil.getNumber( columnType[i],resultSet.getObject(i+2) );
208                                                    // ResultSetValueのカラ?号は?1 する?category ?あるため)
209                                                    value = rsv.getNumber( i+1 );
210                                            }
211                                            catch( SQLException ex ) {              // 6.0.4.0 (2014/11/28) ResultSetValue を使用するので?
212                                                    LogWriter.log( ex );
213                                            }
214                                            catch( RuntimeException ex ) {
215                                                    LogWriter.log( ex );
216                                            }
217    
218                                            clmList[i] = value;
219                                            addValue(value, series[i], category);           // 6.0.2.0 (2014/09/19) columnKeyは、series , rowKey は、category に変更する?
220                                            // Range 求め
221                                            if( value != null ) {
222                                                    final double dbl = value.doubleValue();
223                                                    if( isParetoData ) {                                    // 6.0.2.3 (2014/10/19) パレート図用合?
224                                                            sum += dbl ;
225                                                    } else {
226                                                            if( dbl     < minimum ) { minimum = dbl; }
227                                                            if( maximum < dbl     ) { maximum = dbl; }
228                                                    }
229                                            }
230                                    }
231                                    rowList.add( clmList );
232                                    // 6.0.2.2 (2014/10/03) ColorCategory は、最後?カラ?
233                                    if( isColorCategory ) {
234                                            // 6.0.4.0 (2014/11/28) ResultSetValue を使用するように変更?
235    //                                      String colStr = resultSet.getString(dataSize+2);                // ??カラ?
236                                            final String colStr = rsv.getValue(dataSize+1);                         // ??カラ?
237                                            final Color color   = ColorMap.getColorInstance( colStr );      // 6.0.2.1 (2014/09/26) StringUtil ?ColorMap
238                                            colorList.add( color );
239                                    }
240                            }
241                            numdata = rowList.toArray( new Number[dataSize][rowList.size()] );
242                    }
243                    finally {
244                            Closer.resultClose( resultSet );
245                            Closer.stmtClose( statement );
246                    }
247    
248                    // colorList ?null でな?ど?で判定?
249                    if( isColorCategory && colorList != null ) {
250                            categoryColor = colorList.toArray( new Color[colorList.size()] );
251                    }
252    
253                    // 6.0.2.3 (2014/10/19) パレート図は?00?にする?
254    //              if( isParetoData ) { maximum = changeParetoData(); }
255                    if( isParetoData ) {
256                            changeParetoData( sum );
257                            minimum = 0.0;
258                            maximum = 100.0;
259                    }
260    
261                    range = new Range( minimum, maximum );
262            }
263    
264            /**
265             * DBTableModelオブジェクトから?CategoryDataset の??タを作?します?
266             * openGionの独自処?ソ?です?
267             *
268             * こ?メソ?では、?に #initParam(String[],boolean,isPareto) のパラメータを使用して
269             * 検索した結果の??タを加工、??ます?
270             * また???、データをキャ?ュする事と、データ?を示?レンジオブジェク?を作?します?
271             *
272             * @og.rev 6.0.2.2 (2014/10/03) 新規追?
273             * @og.rev 6.0.2.3 (2014/10/19) パレート図は?00?にする?
274             *
275             * @param table DBTableModelオブジェク?
276             * @see         #execute( Connection,String )
277             */
278            public void execute( final DBTableModel table ) {
279                    final int clmNo = table.getColumnCount();
280                    final int rowNo = table.getRowCount();
281    
282                    // Range を予め求めておきます?
283                    double minimum = Double.POSITIVE_INFINITY;
284                    double maximum = Double.NEGATIVE_INFINITY;
285                    double sum     = 0.0d;                                  // 6.0.2.3 (2014/10/19) パレート図用合?
286    
287                    int dataSize = clmNo -1;                                                // series の個数は、category ?引いた数?
288                    List<Color> colorList = null;                     // 6.0.2.2 (2014/10/03) カ?リカラー
289                    if( isColorCategory ) {                                                 // ColorCategory使用?
290                            colorList       = new ArrayList<Color>();         // カ?リカラー
291                            dataSize--;                                                                     // ?カラ? Colorコードなので、?イナスする?
292                    }
293    
294                    numdata = new Number[rowNo][clmNo];
295    
296                    // ※ DBTableModel の row,col と、Dataset の row,col は??になって?す?
297                    for( int row=0; row<rowNo; row++ ) {
298    //                      String   category = table.getValue( row,0 );                                    // ?番目(アドレス=0)はカラ??設定?
299                            final String   category = uniqCategory( table.getValue( row,0 ) );      // 6.0.2.3 (2014/10/10) categoryの重?避
300                            final String[] vals     = table.getValues( row );
301                            for( int clm=0; clm<dataSize; clm++ ) {
302                                    final String sval = vals[clm+1];                                // ?番目(アドレス=1)からカラ?ータを取?
303                                    final double val  = ( sval == null || sval.isEmpty() ) ? 0.0d : Double.parseDouble( sval ) ;
304    
305                                    addValue( val , seriesLabels[clm] , category );         // val,row,clm
306    //                              numdata[row][clm] = new Double( val );
307                                    numdata[row][clm] = Double.valueOf( val );                      // 6.0.2.4 (2014/10/17) 効??悪?ソ?
308                                    // Range 求め
309                                    if( isParetoData ) {                                    // 6.0.2.3 (2014/10/19) パレート図用合?
310                                            sum += val ;
311                                    } else {
312                                            if( val     < minimum ) { minimum = val; }
313                                            if( maximum < val     ) { maximum = val; }
314                                    }
315                            }
316    
317                            // 6.0.2.2 (2014/10/03) ColorCategory は、最後?カラ?
318                            if( isColorCategory ) {
319                                    final String colStr = vals[dataSize+1];                 // ??カラ?
320                                    final Color color   = ColorMap.getColorInstance( colStr );      // 6.0.2.1 (2014/09/26) StringUtil ?ColorMap
321                                    colorList.add( color );
322                            }
323                    }
324    
325                    // colorList ?null でな?ど?で判定?
326                    if( isColorCategory && colorList != null ) {
327                            categoryColor = colorList.toArray( new Color[colorList.size()] );
328                    }
329    
330                    // 6.0.2.3 (2014/10/19) パレート図は?00?にする?
331    //              if( isParetoData ) { maximum = changeParetoData(); }
332                    if( isParetoData ) {
333                            changeParetoData( sum );
334                            minimum = 0.0;
335                            maximum = 100.0;
336                    }
337    
338                    range = new Range( minimum, maximum );
339            }
340    
341            /**
342             * ?された行?から、数字オブジェクトを取得します?
343             *
344             * @param       row     行番号(シリーズ?横?clm相?
345             * @param       column  カラ?号(カ?リ?縦持ち=row相?
346             *
347             * @return      ??行?の値
348             */
349            @Override
350            public Number getValue( final int row, final int column ) {
351                    // 注意:行?の?が?す?
352                    return numdata[column][row];
353            }
354    
355            /**
356             * レンジオブジェクトを取得します?(独自メソ?)
357             *
358             * @return      レンジオブジェク?
359             */
360            public Range getRange() {
361                    return range;
362            }
363    
364            /**
365             * パレート図用のDatasetに値を書き換えます?(独自メソ?)
366             *
367             * 色?方法?あると思いますが、簡易的に、?部の Number配??
368             * 積上げ計算して、パレート図用の??タを作?します?
369             * レンジオブジェク?も変更します?
370             *
371             * ※ 注意:親クラスの?に持って?実データは変更されて???で?
372             * 場合によっては、おかしな動きをするかもしれません?
373             * そ?場合?、上位にもデータをセ?するように変更する?があります?
374             *
375             * なお?行?の?が、イメージと異なります?で、注意願います?
376             * (columnは、series , row は、category で、シリーズを積み上げま?
377             *
378             * @og.rev 6.0.2.1 (2014/09/26) 新規追?
379             * @og.rev 6.0.2.2 (2014/10/03) HybsDataset i/f
380             * @og.rev 6.0.2.3 (2014/10/19) パレート図は?00?にする?
381             *
382             * @param  sum ??タの合?
383             */
384    //      private double changeParetoData() {
385            private void changeParetoData( final double sum ) {
386    //              if( numdata == null || numdata.length == 0 || numdata[0].length == 0 ) { return 0.0d; }
387                    if( numdata == null || numdata.length == 0 || numdata[0].length == 0 || sum == 0.0 ) { return ; }
388    
389                    final int rowCnt = numdata[0].length ;
390                    final int clmCnt = numdata.length ;
391    
392    //              double maximum = Double.NEGATIVE_INFINITY;
393                    for( int rowNo=0; rowNo<rowCnt; rowNo++ ) {                  // 行?が??
394                            double val = 0.0;               // 初期値
395                            for( int clmNo=0; clmNo<clmCnt; clmNo++ ) {          // 積上げ計算するカラ?ループを回す?
396                                    final Number v1Num = numdata[clmNo][rowNo];
397                                    if(v1Num != null) {
398                                            val += v1Num.doubleValue();                             // 積上げ計算?、?の値のままにしておきます?
399                                    }
400                                    // ??タをセ?するときに?00??します?
401    //                              numdata[clmNo][rowNo] = new Double(val);
402                                    numdata[clmNo][rowNo] = Double.valueOf( Math.round( val * 1000.0 / sum ) / 10.0 );
403            // きちんと計算するなら?BigDecimal で、スケールを指定して四捨五?すべき?・・かも
404            //                      java.math.BigDecimal bd = new BigDecimal( val * 100.0 / sum );
405            //                      numdata[clmNo][rowNo] = bd.setScale( 1, java.math.RoundingMode.HALF_UP );
406                            }
407    //                      if( maximum < val ) { maximum = val; }                       // パレート図用の積上げなので、最大値は、最後???タ
408                    }
409    
410    //              return maximum;
411            }
412    
413            /**
414             * categoryカラー配?を取得します?(独自メソ?)
415             *
416             * こ?クラスは、???カラ?、色??として処?、categoryにColorを指定できます?
417             * select??されて?かった?合?、null を返します?
418             *
419             * select category,series1,series2,series3,???,color from ???
420             *
421             * @og.rev 6.0.2.2 (2014/10/03) 新規追?
422             *
423             * なお?Colorコード?、このクラスで作?しますが、Renderer に与える?があります?
424             * 通常のRenderer には、categoryにカラーを指定する機?がありませんので、HybsBarRenderer に
425             * setCategoryColor( Color[] ) メソ?を用意します?(正確には、HybsDrawItem インターフェース)
426             * こ?Renderer で、getItemPaint( int  , int )メソ?をオーバ?ライドすることで、カ?リごとの
427             * 色を返します?
428             * こ?設定を行うと、シリーズは、カ?リと同?になります?
429             *
430             * @return      categoryカラー配?(なければ null)
431             */
432            public Color[] getCategoryColor() {
433                    // 6.0.2.5 (2014/10/31) refactoring
434    //              return categoryColor;
435                    return ( categoryColor == null ) ? null : categoryColor.clone();
436            }
437    
438            /**
439             * category の重?さけて、?であれば、新しいカ?リ名を作?します?
440             *
441             * カ?リが同じ?合?JFreeChartでは、表示されません。これ?、同じカ?リと認識さ?
442             * 値が上書きされるためです?
443             * こ?問題?、なかなか気づきにくく、デバッグ等に時間がかかってしま?す?
444             * 重?ェ?を行い、警告してもよ??ですが、ここでは、新しいカ?リ名を作?することで
445             * エラーを回避しつつ、とりあえずグラフ表示をするよ?します?
446             *
447             * @og.rev 6.0.2.3 (2014/10/10) 新規追?
448             *
449             * @param       category        ??カ?リ?
450             * @return      新しい??カ?リ?
451             */
452            private String uniqCategory( final String category ) {
453                    String newCate = category ;
454                    int i = 0;
455                    while( !cateCheck.add( newCate ) ) {    // すでに存在して?場合?
456                            newCate = category + "(" + (i++) + ")" ;
457                    }
458    
459                    return newCate ;
460            }
461    
462            /**
463             * こ???と?されたオブジェクトを比?ます?
464             *
465             * 親クラスで、equals メソ?が実?れて?ため、警告がでます?
466             *
467             * @og.rev 5.1.8.0 (2010/07/01) findbug対?
468             * @og.rev 5.1.9.0 (2010/08/01) findbug対?
469             *
470             * @param       object  比?るオブジェク?
471             *
472             * @return      Objectが等し??合? true、そ?な??合? false
473             */
474            @Override
475            public boolean equals( final Object object ) {
476                    if( super.equals( object ) ) {
477                            return hsCode == ((HybsCategoryDataset)object).hsCode;
478                    }
479                    return false;
480            }
481    
482            /**
483             * こ?オブジェクト?ハッシュコードを取得します?
484             *
485             * @og.rev 5.1.8.0 (2010/07/01) findbug対?
486             * @og.rev 5.1.9.0 (2010/08/01) findbug対?
487             *
488             * @return      ハッシュコー?
489             */
490            @Override
491            public int hashCode() { return hsCode ; }
492    }