001package org.opengion.hayabusa.io; 002 003import java.util.concurrent.ConcurrentMap; // 7.0.1.2 (2018/11/04) 004import java.util.concurrent.ConcurrentHashMap; // 7.0.1.2 (2018/11/04) 005 006import org.opengion.fukurou.system.HybsConst ; 007 008/** 009 * JsChartData は、JsChartData の個別属性を管理しているデータ管理クラスです。 010 * 011 * 内部には、data:datasets: の 要素の属性と、options:scales:yAxes: の 要素の属性を管理します。 012 * chartColumn 、useAxis 属性は別管理で、ticks と、gridLines は、関連する属性を無効化します。 013 * datasetOptions と、yAxesOptions は、直接追加されますので、既存の属性をセットしている場合は、 014 * 動作保障できません。 015 * 016 * @og.rev 5.9.17.2 (2017/02/08) 新規作成 017 * @og.rev 7.0.1.1 (2018/10/22) 大幅見直し 018 * 019 * @version 5.9.17.2 2017/02/08 020 * @author T.OTA 021 * @since JDK7.0 022 * 023 */ 024public class JsChartData { 025 /** チャート属性 {@value} */ public static final String DATASET = "dataset"; 026 /** チャート属性 {@value} */ public static final String AXIS = "axis"; 027 /** チャート属性 {@value} */ public static final String TICKS = "ticks"; 028 /** チャート属性 {@value} */ public static final String TIME = "time"; // X軸用 axis属性 029 /** チャート属性 {@value} */ public static final String SCALE_LABEL = "scaleLabel"; 030 /** チャート属性 {@value} */ public static final String GRID_LINES = "gridLines"; 031 032// final int MAX_LEN = SCALE_LABEL.length(); // 暫定的に最も長い文字列 033 034 private final String[] AXIS_OPTS = new String[] { TICKS,TIME,SCALE_LABEL,GRID_LINES } ; // 7.2.9.4 (2020/11/20) private 追加 035 036 private final ConcurrentMap<String,StringBuilder> charts = new ConcurrentHashMap<>(); // 7.0.1.2 (2018/11/04) チャート本体のバッファのMap (not null保障) 037 private final ConcurrentMap<String,StringBuilder> options = new ConcurrentHashMap<>(); // 7.0.1.2 (2018/11/04) オプションバッファのMap (not null保障) 038 039 private String chartColumn ; // チャートカラム 040 private String yid ; // yAxesIDに使用するキーとなるid ( yAxesID=yid+'Ax' ) 041 private boolean useAxis ; // y軸表示を行うかどうか(true/false) 042 private boolean useTime ; // x軸の時間表示を使用するかどうか。 043 044// private final StringBuilder dataset = new StringBuilder( HybsConst.BUFFER_MIDDLE ); 045// private final StringBuilder axis = new StringBuilder( HybsConst.BUFFER_MIDDLE ); 046// private final StringBuilder ticks = new StringBuilder( HybsConst.BUFFER_MIDDLE ); // axis の属性 047// private final StringBuilder scLbl = new StringBuilder( HybsConst.BUFFER_MIDDLE ); // axis の属性 048// private final StringBuilder grdLine = new StringBuilder( HybsConst.BUFFER_MIDDLE ); // axis の属性 049// private final StringBuilder time = new StringBuilder( HybsConst.BUFFER_MIDDLE ); // axis の属性 050 051 /** 052 * デフォルトコンストラクター 053 * 054 * @og.rev 6.9.7.0 (2018/05/14) PMD Each class should declare at least one constructor 055 */ 056 public JsChartData() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 057 058 /** 059 * チャートカラムを設定します。 060 * 061 * @param chartColumn チャートカラム 062 */ 063 public void setChartColumn( final String chartColumn ) { 064 this.chartColumn = chartColumn; 065// addDataset( "data" , chartColumn , true ); // オブジェクトなので、クオート処理しません。 066 } 067 068 /** 069 * JsChartData オブジェクトを作成する時のチャートカラムを取得します。 070 * 071 * @return チャートカラム 072 */ 073 public String getChartColumn() { 074 return chartColumn; 075 } 076 077 /** 078 * データチャートのIDを指定します。 079 * 080 * yAxisIDに使用するキーとなるid ( yAxisID=yid+'Ax' ) 081 * 082 * @og.rev 7.0.1.1 (2018/10/22) 属性の追加。 083 * 084 * @param id 固有の名前 085 */ 086 public void setId( final String id ) { 087 yid = id; 088 089 addAxis( "id" , yid + "Ax" , false ); 090 } 091 092 /** 093 * y軸表示を使用するかどうか(true/false)を設定します。 094 * 095 * 使用するとは、yAxisID属性を、内部的に登録します。 096 * 097 * @param flag true:使用する/false:使用しない 098 */ 099 public void setUseAxis( final boolean flag ) { 100 useAxis = flag; 101 } 102 103 /** 104 * y軸表示を使用するかどうか(true/false)を設定します。 105 * 106 * @return true:使用する/false:使用しない 107 */ 108 public boolean isUseAxis() { 109 return useAxis; 110 } 111 112 /** 113 * x軸の時間表示を使用するかどうか(true/false)を設定します。 114 * 115 * 使用しない場合は、time バッファーを axis 属性に追加しません。 116 * 117 * @param flag true:使用する/false:使用しない 118 */ 119 public void setUseTime( final boolean flag ) { 120 useTime = flag; 121 } 122 123 /** 124 * キーと設定値をdatasetに追加します。 125 * 126 * @param key キー 127 * @param val 設定値(前後のクオーテーション等は、付いているものとします。) 128 * @param isNum 数値項目/boolean項目かどうか(true:数値要素/false:文字または配列要素) 129 */ 130 public void addDataset( final String key , final String val , final boolean isNum ) { 131 addBuffer( DATASET,key,val,isNum ); 132 } 133 134// /** 135// * 引数をdatasetにそのまま追加します。 136// * 137// * @param val キー:設定値や、その他の形式 138// */ 139// public void addDataset( final String val ) { 140// if( val != null && val.length() > 0 ) { 141// dataset.append( val ).append( ',' ); 142// } 143// } 144 145 /** 146 * キーと設定値をaxisに追加します。 147 * 148 * ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。 149 * 150 * @param key キー 151 * @param val 設定値(前後のクオーテーション等は、付いているものとします。) 152 * @param isNum 数値項目かどうか(true:数値要素/false:文字または配列要素) 153 */ 154 public void addAxis( final String key , final String val , final boolean isNum ) { 155 addBuffer( AXIS,key,val,isNum ); 156 } 157 158// /** 159// * 引数をaxisにそのまま追加します。 160// * 161// * ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。 162// * 163// * @param val キー:設定値や、その他の形式 164// */ 165// public void addAxis( final String val ) { 166// if( val != null && val.length() > 0 ) { 167// axis.append( val ).append( ',' ); 168// } 169// } 170 171 /** 172 * キーと設定値をaxisのticks に追加します。 173 * 174 * @param key キー 175 * @param val 設定値(前後のクオーテーション等は、付いているものとします。) 176 * @param isNum 数値項目かどうか(true:数値要素/false:文字または配列要素) 177 */ 178 public void addTicks( final String key , final String val , final boolean isNum ) { 179 addBuffer( TICKS,key,val,isNum ); 180 } 181 182 /** 183 * キーと設定値をaxisのtime に追加します。 184 * 185 * @param key キー 186 * @param val 設定値(前後のクオーテーション等は、付いているものとします。) 187 * @param isNum 数値項目かどうか(true:数値要素/false:文字または配列要素) 188 */ 189 public void addTime( final String key , final String val , final boolean isNum ) { 190 addBuffer( TIME,key,val,isNum ); 191 } 192 193 /** 194 * キーと設定値を指定のバッファーに追加します。 195 * 196 * isNum=true か、内部で、先頭文字が、'[' か '{' の場合は、クオーテーションを付けません。 197 * また、引数が、nullか、空文字列の場合は、追加しません。 198 * 199 * @param bufKey 追加するバッファのキー 200 * @param key キー 201 * @param val 設定値 202 * @param isNum 数値項目かどうか(true:数値要素/false:文字または配列、オブジェクト要素) 203 */ 204 private void addBuffer( final String bufKey , final String key , final String val , final boolean isNum ) { 205 if( val != null && !val.trim().isEmpty() ) { 206 final String val2 = val.trim(); 207 208 // チャート本体のバッファに追加していきます。 209 final StringBuilder buf = charts.computeIfAbsent( bufKey , k -> new StringBuilder( HybsConst.BUFFER_MIDDLE ) ); 210 211 if( isNum || '[' == val2.charAt(0) || '{' == val2.charAt(0) ) { 212 buf.append( key ).append( ':' ).append( val2 ).append( ',' ) ; 213 } 214 else { 215 buf.append( key ).append( ":'" ).append( val2 ).append( "'," ) ; 216 } 217 } 218 } 219 220 /** 221 * 指定のバッファーに、オプション属性を追加します。 222 * 223 * オプション属性は、各バッファーの一番最後にまとめて追加します。 224 * key:val の関係ではなく、val だけをそのまま追加していきます。 225 * オプションの追加は、まとめて最後に行いますので、このメソッド上では 226 * 最後にカンマは付けません。必要であれば、追加する設定値にカンマをつけてください。 227 * 228 * @param bufKey キー [dataset,axis,ticks,time,scaleLabel,gridLines] が指定可能 229 * @param val 設定値 230 */ 231 public void addOptions( final String bufKey , final String val ) { 232 if( val != null && val.length() > 0 ) { 233 // オプション専用のバッファに追加していきます。 234 // これは、チャート本体のバッファに対して、最後に追加する必要があるためです。 235 options.computeIfAbsent( bufKey , k -> new StringBuilder( HybsConst.BUFFER_MIDDLE ) ).append( val ); 236 } 237 } 238 239 /** 240 * バッファキー内に、設定キーの値がすでに登録済みかどうか(あればtrue)を判定します。 241 * 242 * 一般とオプションの両方を検索します。 243 * 244 * @og.rev 7.0.1.3 (2018/11/12) バッファキー検索処理追加 245 * 246 * @param bufKey チェックするバッファのキー 247 * @param key キー 248 * @return すでに登録済みかどうか [true:登録済み/false:未登録] 249 */ 250 public boolean contains( final String bufKey , final String key ) { 251 boolean isContains = false; 252 253 final StringBuilder chBuf = charts.get( bufKey ); 254 if( chBuf != null && chBuf.indexOf( key ) >= 0 ) { isContains = true; } 255 else { 256 final StringBuilder optBuf = options.get( bufKey ); 257 if( optBuf != null && optBuf.indexOf( key ) >= 0 ) { isContains = true; } 258 } 259 260 return isContains ; 261 } 262 263 /** 264 * JsChartData オブジェクトのdata:datasets: パラメータ情報を取得します。 265 * 266 * ここで返す値は、yidが、'y0' とすると、 267 * var y0Ds = { dataset.toString() } ; という文字列を返します。 268 * 引数は、'x' か 'y' を指定します。 269 * 通常、Y軸表示を行う場合は、'y' を指定しまが、horizontalBar 使用時は、 270 * 'x' を指定することになります。 271 * ただし、useAxis=false の場合は、(x,y)AxisID は出力されません。 272 * 273 * @og.rev 7.0.1.1 (2018/10/22) data:datasets: パラメータ情報 274 * 275 * @param xy idのキーワード [x,y] 276 * @return パラメータ文字列 277 */ 278 public String getDataset( final char xy ) { 279 // チャート本体のバッファから取得します。 280 final StringBuilder dataset = charts.computeIfAbsent( DATASET , k -> new StringBuilder( HybsConst.BUFFER_MIDDLE ) ); 281 282 // chartColumn は linear の場合、名前が変更されるので、出力の直前にセッティングします。 283 dataset.append( "data:" ).append( chartColumn ).append( ',' ) ; 284 285 if( useAxis && dataset.indexOf( "AxisID:" ) < 0 ) { 286 dataset.append( xy ).append( "AxisID:'" ).append( getAxisKey() ).append( "'," ); 287 } 288 289 return new StringBuilder( HybsConst.BUFFER_MIDDLE ) 290 .append( "var " ).append( getDatasetKey() ).append( "={" ) 291 .append( dataset ) 292 .append( nval( options , DATASET ) ) // オプション専用のバッファ 293 .append( "};" ).toString(); 294 } 295 296 /** 297 * JsChartData オブジェクトのdata:datasets: パラメータ情報の変数名を取得します。 298 * 299 * ここで返す値は、yidが、'y0' とすると、 300 * "y0Ds" という文字列を返します。 301 * 302 * @og.rev 7.0.1.1 (2018/10/22) data:datasets: パラメータ変数名 303 * 304 * @return パラメータ文字列 305 */ 306 public String getDatasetKey() { 307 return yid + "Ds" ; 308 } 309 310 /** 311 * JsChartData オブジェクトのoptions:scales:yAxes: パラメータ情報を取得します。 312 * 313 * ここで返す値は、yidが、'y0' とすると、 314 * var y0Ax = { addAxis.toString() } ; という文字列を返します。 315 * ただし、useAxis=false の場合は、ゼロ文字列を返します。 316 * 317 * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes: パラメータ情報 318 * 319 * @return パラメータ文字列 320 */ 321 public String getAxis() { 322 // チャート本体のバッファから取得します。 323 final StringBuilder axis = charts.computeIfAbsent( AXIS , k -> new StringBuilder( HybsConst.BUFFER_MIDDLE ) ); 324 325 // AXISのオプションである、TICKS,TIME,SCALE_LABEL,GRID_LINES を追加します。 326 // これらは、チャート本体とオプション専用のバッファから取得しますが、オプション専用バッファは最後に追加します。 327 for( final String opt : AXIS_OPTS ) { 328 // 超特殊処理:useTime=false のときは、TIME は、処理しません。 329 if( !useTime && TIME.equals( opt ) ) { continue; } 330 331 final String key = opt + ":{" ; 332 if( axis.indexOf( key ) < 0 && ( charts.containsKey( opt ) || options.containsKey( opt ) ) ) { 333 axis.append( key ) 334 .append( nval( charts , opt ) ) // チャート本体のバッファ 335 .append( nval( options , opt ) ) // オプション専用のバッファ 336 .append( "}," ); 337 } 338 } 339 340// // チャート本体か、オプションのバッファに、ticks がある場合のみ処理します。 341// if( axis.indexOf( "ticks:{" ) < 0 && ( charts.containsKey( TICKS ) || options.containsKey( TICKS ) ) ) { 342// axis.append( "ticks:{" ) 343// .append( charts.getOrDefault( TICKS , "" ) ) // チャート本体のバッファ 344// .append( options.getOrDefault( TICKS , "" ) ) // オプション専用のバッファ 345// .append( "}," ); 346// } 347// 348// // チャート本体か、オプションのバッファに、time がある場合のみ処理します。 349// if( useTime && axis.indexOf( "time:{" ) < 0 && ( charts.containsKey( TIME ) || options.containsKey( TIME ) ) ) { 350// axis.append( "time:{" ) 351// .append( charts.getOrDefault( TIME , "" ) ) // チャート本体のバッファ 352// .append( options.getOrDefault( TIME , "" ) ) // オプション専用のバッファ 353// .append( "}," ); 354// } 355 356 return new StringBuilder( HybsConst.BUFFER_MIDDLE ) 357 .append( "var " ).append( getAxisKey() ).append( "={" ) 358 .append( axis ) 359 .append( nval( options , AXIS ) ) // オプション専用のバッファ 360 .append( "};" ).toString(); 361 } 362 363 /** 364 * JsChartData オブジェクトのoptions:scales:yAxes: パラメータ情報の変数名を取得します。 365 * 366 * ここで返す値は、yidが、'y0' とすると、 367 * "y0Ax ," という文字列を返します。便宜上、後ろのコロンも追加しています。 368 * その際、useAxis=false の場合は、空文字列を返します。 369 * ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。 370 * 371 * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes:パラメータ情報の変数名 372 * 373 * @return パラメータ文字列 374 */ 375 public String getAxisKey() { 376 return yid + "Ax" ; 377 } 378 379 /** 380 * MapのStringBuilderがnullなら、ゼロ文字列を、そうでなければ、StringBuilder#toString() 381 * の値を返します。 382 * 383 * map.getOrDefault( KEY , new StringBuilder() ) ).toString() 384 * という処理の簡易版です。 385 * 386 * final StringBuilder buf = map.get( KEY ); 387 * return buf == null || buf.length() == 0 ? "" : buf.toString(); 388 * 389 * @og.rev 7.0.1.2 (2018/11/04) 新規登録 390 * 391 * @param map 判定するMap 392 * @param key Mapから取り出すキー 393 * @return MapにStringBuilderがあれば、#toString()を、無ければ、ゼロ文字列を返します。 394 */ 395 private String nval( final ConcurrentMap<String,StringBuilder> map , final String key ) { 396 final StringBuilder buf = map.get( key ); 397 return buf == null || buf.length() == 0 ? "" : buf.toString(); 398 } 399 400// /** 401// * toString() 専用の文字列の長さをあわせるメソッド 402// * 403// * 後ろにスペース埋めします。 404// * 405// * @og.rev 7.0.1.2 (2018/11/04) 新規追加 406// * 407// * @param val そろえる文字列 408// * @param len そろえる文字数 409// * @return 指定の長さにそろえた文字列 410// */ 411// public String getFix( final String val , final int len ) { 412// return ( val + " " ).substring( 0,len ); 413// } 414 415 /** 416 * 内部バッファを文字列にして返します。 417 * 418 * @return 内部バッファを文字列にして返します。 419 * @og.rtnNotNull 420 */ 421 @Override 422 public String toString() { 423 final StringBuilder buf = new StringBuilder( HybsConst.BUFFER_MIDDLE ) 424 .append( "chartColumn=" ).append( chartColumn ).append( HybsConst.CR ) 425 .append( "datasetKey =" ).append( getDatasetKey() ).append( HybsConst.CR ) 426 .append( "axisKey =" ).append( getAxisKey() ).append( HybsConst.CR ); 427 428// charts.forEach( (k,v) -> buf.append( getFix( k ,MAX_LEN+5 ) ).append( " = " ).append( v ).append( HybsConst.CR ) ); 429// options.forEach( (k,v) -> buf.append( getFix( k + " opt" ,MAX_LEN+5 ) ).append( " = " ).append( v ).append( HybsConst.CR ) ); 430 431 charts.forEach( (k,v) -> buf.append( k ).append( " = " ).append( v ).append( HybsConst.CR ) ); 432 options.forEach( (k,v) -> buf.append( k ).append( " opt = " ).append( v ).append( HybsConst.CR ) ); 433 434 return buf.toString(); 435 } 436}