    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
    021// import org.opengion.fukurou.system.HybsConst;
    022import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;                              // 8.0.0.0 (2021/08/31)
    023import static org.opengion.fukurou.system.HybsConst.CR;                                                 // 8.0.0.0 (2021/08/31)
    024
    025/**
    026 * JsChartData は、JsChartData の個別属性を管理しているﾃﾞｰﾀ管理ｸﾗｽです。
    027 *
    028 * 内部には、data:datasets: の 要素の属性と、options:scales:[x/y]: の 要素の属性を管理します。
    029 * chartColumn 、useAxis 属性は別管理で、ticks と、grid:属性 は、関連する属性を無効化します。
    030 * datasetOptions と、yAxesOptions は、直接追加されますので、既存の属性をｾｯﾄしている場合は、
    031 * 動作保障できません。
    032 *
    033 * 8.0.0.0 (2021/08/31) V2 → V3 対応
    034 *  scaleLabel → title
    035 *  gridLines  → grid
    036 *  options       追加
    037 *  plugins       追加(options:plugins:)
    038 *  annotations   追加(plugins:annotation:annotations:)
    039 *
    040 * @og.rev 5.9.17.2 (2017/02/08) 新規作成
    041 * @og.rev 7.0.1.1 (2018/10/22) 大幅見直し
    042 * @og.rev 8.0.0.0 (2021/08/31) Ver3対応 大幅見直し
    043 *
    044 * @version     8.0.0.0
    045 * @author      T.OTA
    046 * @since       JDK11.0
    047 *
    048 */
    049public class JsChartData {
    050        /** ﾁｬｰﾄ属性 {@value} */ public static final String DATASET               = "dataset";
    051        /** ﾁｬｰﾄ属性 {@value} */ public static final String AXIS          = "axis";
    052        /** ﾁｬｰﾄ属性 {@value} */ public static final String TICKS         = "ticks";
    053        /** ﾁｬｰﾄ属性 {@value} */ public static final String TIME          = "time";                               // X軸用 axis属性
    054//      /** ﾁｬｰﾄ属性 {@value} */ public static final String SCALE_LABEL   = "scaleLabel";
    055        /** ﾁｬｰﾄ属性 {@value} */ public static final String TITLE         = "title";                              // 8.0.0.0 (2021/08/31) V2 → V3 対応
    056//      /** ﾁｬｰﾄ属性 {@value} */ public static final String GRID_LINES    = "gridLines";
    057        /** ﾁｬｰﾄ属性 {@value} */ public static final String GRID          = "grid";                               // 8.0.0.0 (2021/08/31) V2 → V3 対応
    058
    059        /** ﾁｬｰﾄ属性 {@value} */ public static final String OPTIONS               = "options";                    // 8.0.0.0 (2021/08/31) V2 → V3 対応
    060        /** ﾁｬｰﾄ属性 {@value} */ public static final String PLUGINS               = "plugins";                    // 8.0.0.0 (2021/08/31) V2 → V3 対応
    061        /** ﾁｬｰﾄ属性 {@value} */ public static final String ANNOTATIONS   = "annotations";                // 8.0.0.0 (2021/08/31) V2 → V3 対応
    062
    063        private static final String CR_TAB = CR + "\t\t";                                                       // 8.0.0.0 (2021/08/31)
    064
    065//      final int MAX_LEN = SCALE_LABEL.length();                                                                       // 暫定的に最も長い文字列
    066
    067//      private final String[] AXIS_OPTS = new String[] { TICKS,TIME,SCALE_LABEL,GRID_LINES } ; // 7.2.9.4 (2020/11/20) private 追加
    068        private final String[] AXIS_OPTS = new String[] { TICKS,TIME,TITLE,GRID } ;                             // 8.0.0.0 (2021/08/31) V2 → V3 対応
    069
    070        private final ConcurrentMap<String,StringBuilder> charts  = new ConcurrentHashMap<>();  // 7.0.1.2 (2018/11/04) ﾁｬｰﾄ本体のﾊﾞｯﾌｧのMap (not null保障)
    071//      private final ConcurrentMap<String,StringBuilder> options = new ConcurrentHashMap<>();  // 7.0.1.2 (2018/11/04) ｵﾌﾟｼｮﾝﾊﾞｯﾌｧのMap (not null保障)
    072
    073        private String  chartColumn                     ;                                                                               // ﾁｬｰﾄｶﾗﾑ
    074        private String  yid                                     ;                                                                               // yAxesIDに使用するキーとなるid ( yAxesID=yid+'Ax' )
    075        private boolean useAxis                         ;                                                                               // y軸表示を行うかどうか(true/false)
    076        private boolean useTime                         ;                                                                               // x軸の時間表示を使用するかどうか。
    077
    078        private final StringBuilder errBuf = new StringBuilder();                                       // 8.0.0.0 (2021/08/31)
    079
    080//      private final StringBuilder dataset = new StringBuilder( BUFFER_MIDDLE );
    081//      private final StringBuilder axis    = new StringBuilder( BUFFER_MIDDLE );
    082//      private final StringBuilder ticks   = new StringBuilder( BUFFER_MIDDLE );       // axis の属性
    083//      private final StringBuilder scLbl   = new StringBuilder( BUFFER_MIDDLE );       // axis の属性
    084//      private final StringBuilder grdLine = new StringBuilder( BUFFER_MIDDLE );       // axis の属性
    085//      private final StringBuilder time    = new StringBuilder( BUFFER_MIDDLE );       // axis の属性
    086
    087        /**
    088         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    089         *
    090         * @og.rev 6.9.7.0 (2018/05/14) PMD Each class should declare at least one constructor
    091         */
    092        public JsChartData() { super(); }       // これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。
    093
    094        /**
    095         * ﾁｬｰﾄｶﾗﾑを設定します。
    096         *
    097         * @param       chartColumn     ﾁｬｰﾄｶﾗﾑ
    098         */
    099        public void setChartColumn( final String chartColumn ) {
    100                this.chartColumn = chartColumn;
    101//              addDataset( "data" , chartColumn , true );      // ｵﾌﾞｼﾞｪｸﾄなので、ｸｵｰﾄ処理しません。
    102        }
    103
    104        /**
    105         * JsChartData ｵﾌﾞｼﾞｪｸﾄを作成する時のﾁｬｰﾄｶﾗﾑを取得します。
    106         *
    107         * @return      ﾁｬｰﾄｶﾗﾑ
    108         */
    109        public String getChartColumn() {
    110                return chartColumn;
    111        }
    112
    113        /**
    114         * ﾃﾞｰﾀﾁｬｰﾄのIDを指定します。
    115         *
    116         * yAxisIDに使用するｷｰとなるid ( yAxisID=yid+'Ax' )
    117         *
    118         * @og.rev 7.0.1.1 (2018/10/22) 属性の追加。
    119         *
    120         * @param       id      固有の名前
    121         */
    122        public void setId( final String id ) {
    123                yid = id;
    124
    125                addAxis( "id" , yid + "Ax" , false );
    126        }
    127
    128        /**
    129         * y軸表示を使用するかどうか(true/false)を設定します。
    130         *
    131         * 使用するとは、yAxisID属性を、内部的に登録します。
    132         *
    133         * @param       flag    true:使用する/false:使用しない
    134         */
    135        public void setUseAxis( final boolean flag ) {
    136                useAxis = flag;
    137        }
    138
    139        /**
    140         * y軸表示を使用するかどうか(true/false)を設定します。
    141         *
    142         * @return      true:使用する/false:使用しない
    143         */
    144        public boolean isUseAxis() {
    145                return useAxis;
    146        }
    147
    148        /**
    149         * x軸の時間表示を使用するかどうか(true/false)を設定します。
    150         *
    151         * 使用しない場合は、time ﾊﾞｯﾌｧｰを axis 属性に追加しません。
    152         *
    153         * @param       flag    true:使用する/false:使用しない
    154         */
    155        public void setUseTime( final boolean flag ) {
    156                useTime = flag;
    157        }
    158
    159        /**
    160         * ｷｰと設定値をdatasetに追加します。
    161         *
    162         * @param       key             ｷｰ
    163         * @param       val             設定値
    164         * @param       isNum   数値項目/boolean項目かどうか(true:数値要素/false:文字または配列要素)
    165         */
    166        public void addDataset( final String key , final String val , final boolean isNum ) {
    167                addBuffer( DATASET,key,val,isNum );
    168        }
    169
    170        /**
    171         * ｷｰと設定値をaxisに追加します。
    172         *
    173         *  ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。
    174         *
    175         * @param       key             ｷｰ
    176         * @param       val             設定値
    177         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    178         */
    179        public void addAxis( final String key , final String val , final boolean isNum ) {
    180                addBuffer( AXIS,key,val,isNum );
    181        }
    182
    183        /**
    184         * 設定値をaxisに追加します。
    185         *
    186         * これは、chartsﾊﾞｯﾌｧに、bufKey 毎のﾊﾞｯﾌｧに、引数をそのまま追加します。
    187         *
    188         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
    189         *
    190         * @param       bufKey  追加するﾊﾞｯﾌｧのｷｰ
    191         * @param       val             設定値
    192         */
    193        public void addAxis( final String bufKey , final String val ) {
    194                if( val != null && val.length() > 0 ) {
    195                        // ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
    196                        final StringBuilder buf = charts.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );
    197                        // 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
    198                        final int st = val.indexOf( ':' );
    199                        if( st > 0 && buf.indexOf( val.substring( 0,st+1 ) ) >= 0 ) {           // ｷｰﾁｪｯｸは、':' も含めて確認する
    200                                errBuf.append( "addAxisで登録された" ).append( bufKey )
    201                                                .append( "属性は、chartsに設定済みです。" ).append( CR )
    202                                                .append( "val=" ).append( val ).append( CR );
    203                        }
    204                        else {
    205                                buf.append( val ).append( ',' );
    206                        }
    207                }
    208        }
    209
    210        /**
    211         * ｷｰと設定値をaxisのticks に追加します。
    212         *
    213         * @param       key             ｷｰ
    214         * @param       val             設定値
    215         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    216         */
    217        public void addTicks( final String key , final String val , final boolean isNum ) {
    218                addBuffer( TICKS,key,val,isNum );
    219        }
    220
    221        /**
    222         * ｷｰと設定値をaxisのtime に追加します。
    223         *
    224         * @param       key             ｷｰ
    225         * @param       val             設定値
    226         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    227         */
    228        public void addTime( final String key , final String val , final boolean isNum ) {
    229                addBuffer( TIME,key,val,isNum );
    230        }
    231
    232        /**
    233         * ｷｰと設定値をplugins に追加します。
    234         *
    235         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
    236         *
    237         * @param       key             ｷｰ
    238         * @param       val             設定値
    239         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    240         */
    241        public void addPlugins( final String key , final String val , final boolean isNum ) {
    242                addBuffer( PLUGINS,key,val,isNum );
    243        }
    244
    245        /**
    246         * ｷｰと設定値をannotations に追加します。
    247         *
    248         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
    249         *
    250         * @param       key             ｷｰ
    251         * @param       val             設定値
    252         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    253         */
    254        public void addAnnotations( final String key , final String val , final boolean isNum ) {
    255                addBuffer( ANNOTATIONS,key,val,isNum );
    256        }
    257
    258        /**
    259         * ｷｰと設定値を指定のﾊﾞｯﾌｧｰに追加します。
    260         *
    261         * isNum=true か、内部で、先頭文字が、'[' か '{' の場合は、ｸｵｰﾃｰｼｮﾝを付けません。
    262         * また、引数が、nullか、空文字列の場合は、追加しません。
    263         *
    264         * @param       bufKey  追加するﾊﾞｯﾌｧのｷｰ
    265         * @param       key             ｷｰ
    266         * @param       val             設定値
    267         * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列、ｵﾌﾞｼﾞｪｸﾄ要素)
    268         */
    269        private void addBuffer( final String bufKey , final String key , final String val , final boolean isNum ) {
    270                if( val != null && !val.trim().isEmpty() ) {
    271                        final String val2 = val.trim();
    272
    273                        // ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
    274                        final StringBuilder buf = charts.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );
    275
    276                        // 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
    277                        if( buf.indexOf( key+':' ) >= 0 ) {                             // ｷｰﾁｪｯｸは、':' も含めて確認する
    278                                errBuf.append( "addBufferで登録された" ).append( bufKey ).append( ':' ).append( key )
    279                                                .append( "属性は、chartsに設定済みです。" ).append( CR )
    280                                                .append( "key:val=" ).append( key ).append( ':' ).append( val2 ).append( CR );
    281                        }
    282                        else {
    283                                // bufKey が DATASET とAXIS の場合は、40文字単位で改行する。
    284                                if( DATASET.equals( bufKey ) ||  AXIS.equals( bufKey ) ) {
    285                                        if( buf.length() - buf.lastIndexOf( CR ) > 40 ) {
    286                                                buf.append( CR_TAB );
    287                                        }
    288                                }
    289
    290                                if( isNum || '[' == val2.charAt(0) || '{' == val2.charAt(0) ) {
    291                                        buf.append( key ).append( ':' ).append( val2 ).append( ',' );
    292                                }
    293                                else {
    294                                        buf.append( key ).append( ":'" ).append( val2 ).append( "'," );
    295                                }
    296                        }
    297                }
    298        }
    299
    300//      /**
    301//       * 指定のﾊﾞｯﾌｧｰに、ｵﾌﾟｼｮﾝ属性を追加します。
    302//       *
    303//       * ｵﾌﾟｼｮﾝ属性は、各ﾊﾞｯﾌｧｰの一番最後にまとめて追加します。
    304//       * key:val の関係ではなく、val だけをそのまま追加していきます。
    305//       * ｵﾌﾟｼｮﾝの追加は、まとめて最後に行いますので、このﾒｿｯﾄﾞ上では
    306//       * 最後にｶﾝﾏは付けません。必要であれば、追加する設定値にｶﾝﾏをつけてください。
    307//       *
    308//       * @og.rev 8.0.0.0 (2021/08/31) ｺﾒﾝﾄ修正(scaleLabel→title , gridLines→grid)
    309//       *
    310//       * @param       bufKey  ｷｰ [dataset,axis,ticks,time,scaleLabel,gridLines] が指定可能
    311//       * @param       val             設定値
    312//       */
    313//      public void addOptions( final String bufKey , final String val ) {
    314//              if( val != null && val.length() > 0 ) {
    315//                      // ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧに追加していきます。
    316//                      // これは、ﾁｬｰﾄ本体のﾊﾞｯﾌｧに対して、最後に追加する必要があるためです。
    317////                    options.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) )
    318////                                            .append( val ).append( ',' );
    319//
    320//                      // ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧに追加していきます。
    321//                      final StringBuilder buf = options.computeIfAbsent( bufKey , k -> new StringBuilder( BUFFER_MIDDLE ) );
    322//
    323//                      // 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
    324//                      final int st = val.indexOf( ':' );
    325//                      if( st > 0 && buf.indexOf( val.substring( 0,st+1 ) ) >= 0 ) {   // ｷｰﾁｪｯｸは、':' も含めて確認する
    326//                              errBuf.append( "addOptionsで登録された" ).append( bufKey )
    327//                                              .append( "属性は、options に設定済みです。" ).append( CR )
    328//                                              .append( "val=" ).append( val ).append( CR );
    329//                      }
    330//                      else {
    331//                              buf.append( val ).append( ',' ).append( CR_TAB );
    332//                      }
    333//              }
    334//      }
    335
    336//      /**
    337//       * ｷｰと設定値をoptions に追加します。
    338//       *
    339//       * @og.rev 8.0.0.0 (2021/08/31) 新規作成
    340//       *
    341//       * @param       key             ｷｰ
    342//       * @param       val             設定値
    343//       * @param       isNum   数値項目かどうか(true:数値要素/false:文字または配列要素)
    344//       */
    345//      public void addOptions( final String key , final String val , final boolean isNum ) {
    346//              if( val != null && !val.trim().isEmpty() ) {
    347//                      final String val2 = val.trim();
    348//
    349//                      // ﾁｬｰﾄ本体のﾊﾞｯﾌｧに追加していきます。
    350//                      final StringBuilder buf = options.computeIfAbsent( key , k -> new StringBuilder( BUFFER_MIDDLE ) );
    351//
    352//                      // 登録時の同一ｷｰﾜｰﾄﾞﾁｪｯｸ
    353//                      if( buf.indexOf( key+':' ) >= 0 ) {     // ｷｰﾁｪｯｸは、':' も含めて確認する
    354//                              errBuf.append( "addOptionsで登録された" ).append( key )
    355//                                              .append( "属性は、optionsに設定済みです。" ).append( CR )
    356//                                              .append( "key:val=" ).append( key ).append( ':' ).append( val2 ).append( CR );
    357//                      }
    358//                      else {
    359//                              if( isNum || '[' == val2.charAt(0) || '{' == val2.charAt(0) ) {
    360//                                      buf.append( key ).append( ':' ).append( val2 ).append( ',' ) ;
    361//                              }
    362//                              else {
    363//                                      buf.append( key ).append( ":'" ).append( val2 ).append( "'," ) ;
    364//                              }
    365//                      }
    366//              }
    367//      }
    368
    369        /**
    370         * ﾊﾞｯﾌｧｷｰ内に、設定ｷｰの値がすでに登録済みかどうか(あればtrue)を判定します。
    371         *
    372         * 一般とｵﾌﾟｼｮﾝの両方を検索します。
    373         *
    374         * @og.rev 7.0.1.3 (2018/11/12) ﾊﾞｯﾌｧｷｰ検索処理追加
    375         *
    376         * @param       bufKey  ﾁｪｯｸするﾊﾞｯﾌｧのｷｰ
    377         * @param       key             ｷｰ
    378         * @return      すでに登録済みかどうか [true:登録済み/false:未登録]
    379         */
    380        public boolean contains( final String bufKey , final String key ) {
    381                boolean isContains = false;
    382
    383                final StringBuilder chBuf = charts.get( bufKey );
    384                if( chBuf != null && chBuf.indexOf( key ) >= 0 ) { isContains = true; }
    385//              else {
    386//                      final StringBuilder optBuf = options.get( bufKey );
    387//                      if( optBuf != null && optBuf.indexOf( key ) >= 0 ) { isContains = true; }
    388//              }
    389
    390                return isContains ;
    391        }
    392
    393        /**
    394         * JsChartData ｵﾌﾞｼﾞｪｸﾄのdata:datasets: ﾊﾟﾗﾒｰﾀ情報を取得します。
    395         *
    396         * ここで返す値は、yidが、'y0' とすると、
    397         * const y0Ds = { dataset.toString() } ; という文字列を返します。
    398         * 引数は、'x' か 'y' を指定します。
    399         * 通常、Y軸表示を行う場合は、'y' を指定しまが、horizontalBar 使用時は、
    400         * 'x' を指定することになります。
    401         * ただし、useAxis=false の場合は、(x,y)AxisID は出力されません。
    402         *
    403         * @og.rev 7.0.1.1 (2018/10/22) data:datasets: ﾊﾟﾗﾒｰﾀ情報
    404         *
    405         * @param       xy      idのｷｰﾜｰﾄﾞ [x,y]
    406         * @return      ﾊﾟﾗﾒｰﾀ文字列
    407         */
    408        public String getDataset( final char xy ) {
    409                // ﾁｬｰﾄ本体のﾊﾞｯﾌｧから取得します。
    410                final StringBuilder dataset = charts.computeIfAbsent( DATASET , k -> new StringBuilder( BUFFER_MIDDLE ) );
    411
    412                // chartColumn は linear の場合、名前が変更されるので、出力の直前にｾｯﾃｨﾝｸﾞします。
    413                dataset.append( "data:" ).append( chartColumn ).append( ',' ) ;
    414
    415                // 8.0.0.0 (2021/08/31) yidの初期値(1つ目)は、AxisID を出さない。
    416//              if( useAxis && dataset.indexOf( "AxisID:" ) < 0 ) {
    417                if( useAxis && dataset.indexOf( "AxisID:" ) < 0 && !"y0".equals( yid ) ) {
    418                        dataset.append( xy ).append( "AxisID:'" ).append( getAxisKey() ).append( "'," );
    419                }
    420
    421                return new StringBuilder( BUFFER_MIDDLE )
    422//                                      .append( "var " ).append( getDatasetKey() ).append( "={" )
    423                                        .append( "const " ).append( getDatasetKey() ).append( "={" )
    424                                        .append( dataset )
    425//                                      .append( mapGet( options , DATASET ) )          // ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧ
    426                                        .append( CR ).append( "\t};" ).toString();
    427        }
    428
    429        /**
    430         * JsChartData ｵﾌﾞｼﾞｪｸﾄのdata:datasets: ﾊﾟﾗﾒｰﾀ情報の変数名を取得します。
    431         *
    432         * ここで返す値は、yidが、'y0' とすると、
    433         * "y0Ds" という文字列を返します。
    434         *
    435         * @og.rev 7.0.1.1 (2018/10/22) data:datasets: ﾊﾟﾗﾒｰﾀ変数名
    436         *
    437         * @return      ﾊﾟﾗﾒｰﾀ文字列
    438         */
    439        public String getDatasetKey() {
    440                return yid + "Ds" ;
    441        }
    442
    443        /**
    444         * JsChartData ｵﾌﾞｼﾞｪｸﾄのoptions:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報を取得します。
    445         *
    446         * ここで返す値は、yidが、'y0' とすると、
    447         * const y0Ax = { addAxis.toString() } ; という文字列を返します。
    448         * ただし、useAxis=false の場合は、ｾﾞﾛ文字列を返します。
    449         *
    450         * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報
    451         *
    452         * @return      ﾊﾟﾗﾒｰﾀ文字列
    453         */
    454        public String getAxis() {
    455                // ﾁｬｰﾄ本体のﾊﾞｯﾌｧから取得します。
    456                final StringBuilder axis = charts.computeIfAbsent( AXIS , k -> new StringBuilder( BUFFER_MIDDLE ) );
    457
    458                // AXISのｵﾌﾟｼｮﾝである、TICKS,TIME,TITLE,GRID を追加します。
    459                // これらは、ﾁｬｰﾄ本体とｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧから取得しますが、ｵﾌﾟｼｮﾝ専用ﾊﾞｯﾌｧは最後に追加します。
    460                for( final String opt : AXIS_OPTS ) {
    461                        // 超特殊処理：useTime=false のときは、TIME は、処理しません。
    462                        if( !useTime && TIME.equals( opt ) ) { continue; }
    463
    464                        final String key = opt + ":{" ;
    465//                      if( axis.indexOf( key ) < 0 && ( charts.containsKey( opt ) || options.containsKey( opt ) ) ) {
    466                        if( axis.indexOf( key ) < 0 && charts.containsKey( opt ) ) {
    467                                axis.append( CR_TAB ).append( key )
    468                                        .append( mapGet( charts  , opt ) )                      // ﾁｬｰﾄ本体のﾊﾞｯﾌｧ
    469//                                      .append( mapGet( options , opt ) )                      // ｵﾌﾟｼｮﾝ専用のﾊﾞｯﾌｧ
    470                                        .append( "}," );
    471                        }
    472                }
    473
    474                return new StringBuilder( BUFFER_MIDDLE )
    475//                                      .append( "var " ).append( getAxisKey() ).append( "={" )
    476                                        .append( "const " ).append( getAxisKey() ).append( "={" )
    477                                        .append( axis )
    478//                                      .append( mapGet( options , AXIS ) )                     // オプション専用のバッファ
    479                                        .append( CR ).append( "\t};" ).toString();
    480        }
    481
    482        /**
    483         * JsChartData ｵﾌﾞｼﾞｪｸﾄのoptions:scales:yAxes: ﾊﾟﾗﾒｰﾀ情報の変数名を取得します。
    484         *
    485         * ここで返す値は、yidが、'y0' とすると、
    486         * "y0Ax ," という文字列を返します。便宜上、後ろのｺﾛﾝも追加しています。
    487         * その際、useAxis=false の場合は、空文字列を返します。
    488         *  ※ chartJS上は、Axes(axisの複数形)と、Axis を使い分けていますが、属性は、axis で統一します。
    489         *
    490         * @og.rev 7.0.1.1 (2018/10/22) options:scales:yAxes:ﾊﾟﾗﾒｰﾀ情報の変数名
    491         *
    492         * @return      ﾊﾟﾗﾒｰﾀ文字列
    493         */
    494        public String getAxisKey() {
    495                return yid + "Ax" ;
    496        }
    497
    498        /**
    499         * MapのStringBuilderがnullなら、ｾﾞﾛ文字列を、そうでなければ、StringBuilder#toString()
    500         * の値を返します。
    501         *
    502         * map.getOrDefault( KEY , new StringBuilder() ) ).toString()
    503         * という処理の簡易版です。
    504         *
    505         * final StringBuilder buf = map.get( KEY );
    506         * return buf == null || buf.length() == 0 ? "" : buf.toString();
    507         *
    508         * @og.rev 7.0.1.2 (2018/11/04) 新規登録
    509         *
    510         * @param       map     判定するMap
    511         * @param       key     Mapから取り出すｷｰ
    512         * @return      MapにStringBuilderがあれば、#toString()を、無ければ、ｾﾞﾛ文字列を返します。
    513         */
    514//      private String nval( final ConcurrentMap<String,StringBuilder> map , final String key ) {
    515        private String mapGet( final ConcurrentMap<String,StringBuilder> map , final String key ) {
    516                final StringBuilder buf = map.get( key );
    517                return buf == null || buf.length() == 0 ? "" : buf.toString();
    518        }
    519
    520        /**
    521         * ｴﾗｰﾒｯｾｰｼﾞを返します。
    522         *
    523         * ｴﾗｰが存在しなかった場合は、長さｾﾞﾛの文字列になります。
    524         *
    525         * @og.rev 8.0.0.0 (2021/08/31) 新規作成
    526         *
    527         * @return      ｴﾗｰﾒｯｾｰｼﾞの内部ﾊﾞｯﾌｧを文字列にして返します。
    528         * @og.rtnNotNull
    529         */
    530        public String getErrorMessage() {
    531                return errBuf.toString();
    532        }
    533
    534        /**
    535         * 内部ﾊﾞｯﾌｧを文字列にして返します。
    536         *
    537         * @return      内部ﾊﾞｯﾌｧを文字列にして返します。
    538         * @og.rtnNotNull
    539         */
    540        @Override
    541        public String toString() {
    542                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
    543                        .append( "chartColumn=" ).append( chartColumn     ).append( CR )
    544                        .append( "datasetKey =" ).append( getDatasetKey() ).append( CR )
    545                        .append( "axisKey    =" ).append( getAxisKey()    ).append( CR );
    546
    547                charts.forEach(  (k,v) -> buf.append( k ).append( " = "     ).append( v ).append( CR ) );
    548//              options.forEach( (k,v) -> buf.append( k ).append( " opt = " ).append( v ).append( CR ) );
    549
    550                return buf.toString();
    551        }
    552}