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