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 }