    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.fukurou.util;
    017
    018import java.util.Iterator;
    019import java.util.Map;
    020import java.util.HashMap;
    021
    022import org.opengion.fukurou.system.HybsConst;
    023
    024/**
    025 * JSONScan.java は、JSONで行うためのUtilクラスです。
    026 *
    027 * @og.rev 8.0.2.0 (2021/11/30) 新規作成
    028 * @og.group ユーティリティ
    029 *
    030 * @version  8.0
    031 * @author   LEE.
    032 * @since    JDK17.0,
    033 */
    034public final class JSONScan implements Iterator<String> {
    035        private static final String CR                  = HybsConst.CR;                                         // システムの改行コード
    036        private static final int BUFFER_MIDDLE  = HybsConst.BUFFER_MIDDLE;                      // StringBilderなどの初期値
    037
    038        private final String            orgStr;                                                                                 // 処理対象の文字列
    039        private final char                      stCh;                                                                                   // 開始文字
    040        private final char                      edCh;                                                                                   // 終了文字
    041        private final int                       stScan;                                                                                 // 検索範囲の開始位置
    042        private final int                       edScan;                                                                                 // 検索範囲の終了位置
    043        private int                                     stAdrs;                                                                                 // 処理途中の切り出し開始位置
    044        private int                                     edAdrs;                                                                                 // 処理途中の切り出し終了位置
    045
    046        /**
    047         * コンストラクタ
    048         * 処理対象の文字列を解析する JSONScan のインスタンスを作成します。
    049         * 検索範囲の開始位置は、ゼロ(0) で初期化されます。
    050         * 検索範囲の終了位置は、文字列の長さ で初期化されます。
    051         *
    052         * @param       orgStr  処理対象の文字列
    053         * @param       stCh    開始文字
    054         * @param       edCh    終了文字
    055         */
    056        public JSONScan( final String orgStr, final char stCh, final char edCh ) {
    057                this( orgStr, stCh, edCh, 0, orgStr.length() );
    058        }
    059
    060        /**
    061         * コンストラクタ
    062         * 処理対象の文字列を解析する JSONScan のインスタンスを作成します。
    063         *
    064         * @param       orgStr  処理対象の文字列
    065         * @param       stCh    開始文字
    066         * @param       edCh    終了文字
    067         * @param       stScan  検索範囲の開始位置
    068         * @param       edScan  検索範囲の終了位置
    069         */
    070        public JSONScan( final String orgStr, final char stCh, final char edCh , final int stScan , final int edScan ) {
    071                this.orgStr                             = orgStr;                                                                               // 処理対象の文字列
    072                this.stCh                               = stCh;                                                                                 // 開始文字
    073                this.edCh                               = edCh;                                                                                 // 終了文字
    074                this.stScan                             = stScan;                                                                               // 検索範囲の開始位置
    075                this.edScan                             = edScan;                                                                               // 検索範囲の終了位置
    076                stAdrs                                  = 0;                                                                                    // 処理途中の切り出し開始位置
    077                edAdrs                                  = stScan;                                                                               // 処理途中の切り出し終了位置(検索範囲の最初のアドレス)
    078        }
    079
    080        /**
    081         * 検索範囲から開始文字がまだあるかどうかを判定します。
    082         * このメソッドが true を返す場合、それ以降の引数のない next() への
    083         * 呼び出しは適切に文字列を返します。
    084         *
    085         * @return      文字列内の現在の位置の後ろに 1 つ以上の開始文字がある場合だけ true、そうでない場合は false
    086         */
    087        @Override       // Iterator
    088        public boolean hasNext() {
    089                stAdrs = orgStr.indexOf( stCh, edAdrs );
    090                // 見つからないか、検索範囲を超えた場合
    091                if( stAdrs < 0 || stAdrs > edScan ) {
    092                        return false;
    093                } else {
    094                        edAdrs = orgStr.indexOf( edCh , stAdrs+1 );
    095                        // 見つからないか、検索範囲を超えた場合
    096                        if( edAdrs < 0 || edAdrs > edScan ) {
    097                                return false;
    098                        }
    099                }
    100                return true;
    101        }
    102
    103        /**
    104         * 検索範囲から次の文字列を返します。
    105         *
    106         * @return      処理途中の切り出し文字列
    107        */
    108        @Override       // Iterator
    109        public String next() {
    110                return orgStr.substring( stAdrs+1 , edAdrs );
    111        }
    112
    113        /**
    114         * 検索範囲から開始文字の繰り返し数を数えます。
    115         *
    116         * @return      開始文字の出現回数
    117         */
    118        public int countBlock() {
    119                final String subStr = orgStr.substring( stScan , edScan );
    120                final long cnt = subStr.chars().filter(ch -> ch == stCh).count();
    121                return Math.toIntExact(cnt);
    122        }
    123
    124        /**
    125         * JSON形式 から Mapオブジェクト に変更します。
    126         *
    127         * 「カンマ(,)」と「コロン(:)」キーワードで分割し、キーとラベルのペア情報を
    128         * マッピングします。
    129         *
    130         * @param       csvData JSON形式の文字列 (例：{"key1":"value1","key2":"value2"})
    131         * @return      Mapオブジェクト
    132         */
    133        public static Map<String,String> json2Map( final String csvData ) {
    134                final Map<String,String> map = new HashMap<>();
    135                // 「カンマ(,)」で分割
    136                final String[] ary = StringUtil.csv2Array( csvData );
    137                for( final String str : ary ) {
    138                        // 「コロン(:)」で分割
    139                        final String[] kv = str.split(":", 2);
    140                        if( kv.length == 2 ) {
    141                                map.put( json2Trim(kv[0]), json2Trim(kv[1]) );
    142                        }
    143                }
    144                return map;
    145        }
    146
    147        /**
    148         * Mapオブジェクト から JSON形式 に変更します。
    149         *
    150         * @param       prmMap  Mapオブジェクト
    151         * @return      JSON形式の文字列 (例：{"key1":"value1","key2":"value2"})
    152         */
    153        public static String map2Json( final Map<String,String> prmMap ) {
    154                final StringBuilder buf = new StringBuilder(BUFFER_MIDDLE);
    155                if( prmMap != null ) {
    156                        int i = 0;
    157                        buf.append( '{' )
    158                                .append( CR );
    159
    160                        for( final Map.Entry<String, String> entry : prmMap.entrySet() ) {
    161                                if( i == 0 ) { buf.append( '"' ); }
    162                                else { buf.append( ",\"" ); }
    163                                i++;
    164
    165                                buf.append( entry.getKey() )
    166                                        .append( "\":\"" )
    167                                        .append( entry.getValue() )
    168                                        .append( '"' )
    169                                        .append( CR );
    170                        }
    171                        buf.append( '}' );
    172                }
    173                return buf.toString();
    174        }
    175
    176        /**
    177         * 「カンマ(,)」区切り文字で連結された String を、配列に分解して、その値を返します。
    178         * ヌル値(null)は 空白 に変換した値を返します。
    179         *
    180         * @param       csvData 元のデータ
    181         * @return      文字列配列
    182         */
    183        public static String[] json2Array( final String csvData ) {
    184                final String[] ary = StringUtil.csv2Array( csvData );
    185                for( int i=0; i<ary.length; i++ ) {
    186                        ary[i] = "null".equals(ary[i]) ? "" : json2Trim( ary[i] ) ;
    187                }
    188                return ary;
    189        }
    190
    191        // 8.1.1.0 (2022/02/04) 大括弧([])と制御文字(改行等)も削除対象にします。
    192        private static final String JSON_TRIM = "\"{}[] ";
    193
    194        /**
    195//       * 前後の二重引用符(")、中括弧({})、スペースを削除します。
    196         * 前後の二重引用符(")、中括弧({})、大括弧([])、スペース、制御文字を削除します。
    197         *
    198         * @og.rev 8.1.1.0 (2022/02/04) 大括弧([])と制御文字(改行等)も削除対象にします。
    199         *
    200         * @param       str     文字列
    201//       * @return      前後の二重引用符(")、中括弧({})、スペースを削除した文字列
    202         * @return      前後の二重引用符(")、中括弧({})、大括弧([])、スペース、制御文字を削除した文字列
    203         */
    204        private static String json2Trim( final String str ) {
    205                int st = 0;
    206                int ed = str.length();
    207                while( st < ed ) {
    208                        final char ch = str.charAt(st);
    209//                      if( ch == '"' || ch == '{'  || ch == '}' || ch == ' ' ) { st++; }
    210                        if( JSON_TRIM.indexOf(ch) >= 0 || ch < ' ' ) { st++; }          // 8.1.1.0 (2022/02/04)
    211                        else { break; }
    212                }
    213                while( st < ed ) {
    214                        final char ch = str.charAt(ed-1);
    215//                      if( ch == '"' || ch == '{'  || ch == '}' || ch == ' ' ) { ed--; }
    216                        if( JSON_TRIM.indexOf(ch) >= 0 || ch < ' ' ) { ed--; }          // 8.1.1.0 (2022/02/04)
    217                        else { break; }
    218                }
    219                return st < ed ? str.substring(st, ed) : "" ;
    220        }
    221}