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}