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 }