View Javadoc
1   package com.github.mygreen.cellformatter.callback;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.List;
6   import java.util.regex.Matcher;
7   import java.util.regex.Pattern;
8   
9   import com.github.mygreen.cellformatter.lang.ArgUtils;
10  
11  /**
12   * 数字の表現を漢数字に変換する。
13   * <p>数字が連続しないと、桁数を考慮した変換はできない。</p>
14   * <p>途中に区切り文字などがあると、そこで途切れる。</p>
15   * <p>大字の定義:<a href="http://www.benricho.org/kanji/kansuji.html">漢数字と大字〔だいじ〕の書き方</a></p>
16   *
17   * @since 2.0
18   * @author T.TSUCHIE
19   *
20   */
21  public class KansujiConverter {
22  
23      /**
24       * 整数部分の切り出し用正規表現
25       */
26      protected static final Pattern PATTERN_NUM = Pattern.compile("([\\D]*)([\\d]+)([\\.]{0,1}[.\\s\\w]*)");
27  
28      /**
29       * 0~9の数字のマップ
30       */
31      protected String[] numMap = {
32          "〇",
33          "一",
34          "二",
35          "三",
36          "四",
37          "五",
38          "六",
39          "七",
40          "八",
41          "九"
42      };
43  
44      /**
45       * 10^4ごとの桁単位のマップ
46       * <p><a href="http://www2s.biglobe.ne.jp/~hotori/kazu.html">桁数の名前</a>
47       */
48      protected String[] digits10Map = {
49          "",      // 10^0
50          "万",    // 10^4
51          "億",    // 10^8
52          "兆",    // 10^12
53          "京",    // 10^16
54      };
55  
56      /**
57       * 4桁の桁単位のマップ
58       */
59      protected String[] digits4Map = {
60          "",
61          "十",
62          "百",
63          "千",
64      };
65  
66      /**
67       * 文字列を変換する
68       * @param value 変換対象の値
69       * @param is4YearTerm 4桁の年指定の項の場合
70       * @return 変換後の値
71       */
72      public String convert(final String value, final boolean is4YearTerm) {
73  
74          final Matcher matcher = PATTERN_NUM.matcher(value);
75          if(!matcher.matches()) {
76              // 一致しない場合は、単純に数値の変換
77              return replaceSimple(value);
78          }
79  
80          final String before = matcher.group(1);
81          final String num = matcher.group(2);
82          final String after = matcher.group(3);
83  
84          if(is4YearTerm && num.length() == 4) {
85              // 年の桁は、単純に数値変換する
86              return replaceSimple(before) + replaceSimple(num) + replaceSimple(after);
87  
88          } else {
89              return replaceSimple(before) + replaceDisits(num) + replaceSimple(after);
90  
91          }
92  
93      }
94  
95      /**
96       * 数字を単純に変換する。
97       * @param value 変換対象の文字列
98       * @return
99       */
100     protected String replaceSimple(final String value) {
101         String str = value;
102         for(int i=0; i < numMap.length; i++) {
103             str = str.replaceAll(String.valueOf(i), numMap[i]);
104         }
105 
106         return str;
107     }
108 
109     /**
110      * 数字を桁数に合わせて変換する。
111      * @param value
112      * @return
113      */
114     protected String replaceDisits(final String value) {
115 
116         if(value.equals("0")) {
117             return numMap[0];
118         }
119 
120         // 4桁ごとに、分割する。
121         final int length = value.length();
122         final List<String> split4 = new ArrayList<>();
123         for(int i=0; i < length; i=i+4) {
124 
125             // 下の桁から切り出す
126             int end = length -i;
127             int start;
128             if(i + 4 < length) {
129                 start = end - 4;
130             } else {
131                 start = 0;
132             }
133             String item = value.substring(start, end);
134             split4.add(item);
135         }
136 
137         // 4桁ごとに変換を行う。
138         final List<String> digits = new ArrayList<>();
139         for(int i=0; i < split4.size(); i++) {
140             String item = split4.get(i);
141             item = replace4Digits(item) + digits10Map[i];
142 
143             digits.add(item);
144         }
145 
146         /*
147          * 文字列に直す。
148          * ・桁数が逆順になっているので、戻し結合する。
149          */
150         Collections.reverse(digits);
151         StringBuilder sb = new StringBuilder();
152         for(String item : digits) {
153             sb.append(item);
154         }
155 
156         return sb.toString();
157     }
158 
159     /**
160      * 4桁以下の数字を、漢数字に変換する。
161      * @param value
162      * @return
163      */
164     protected String replace4Digits(final String value) {
165 
166         // 桁ごとに変換を行う。
167         final int length = value.length();
168         final List<String> digits = new ArrayList<>();
169 
170         for(int i=0; i < length; i++) {
171             // 下の桁から処理する
172             final char c = value.charAt(length-i-1);
173             if(c == '0') {
174                 continue;
175 
176             }
177 
178             String item;
179             if(c == '1' && i > 0) {
180                 // 10の位以上で、かつ1の場合、数字部分は省略し、桁数のみにする。
181                 item = digits4Map[i];
182             } else {
183                 item = replaceSimple(String.valueOf(c)) + digits4Map[i];
184             }
185 
186             digits.add(item);
187 
188         }
189 
190         /*
191          * 文字列に直す。
192          * ・桁数が逆順になっているので、戻し結合する。
193          */
194         Collections.reverse(digits);
195         StringBuilder sb = new StringBuilder();
196         for(String item : digits) {
197             sb.append(item);
198         }
199 
200         return sb.toString();
201 
202     }
203 
204     /**
205      * 0~9の数字のマップを設定する
206      * @param numMap 配列のインデックスに対応する数字のマップ
207      * @throws IllegalArgumentException {@literal numMap size != 10}
208      */
209     public void setNumMap(String[] numMap) {
210         ArgUtils.notEmpty(numMap, "numMap");
211 
212         if(numMap.length != 10) {
213             throw new IllegalArgumentException("numMap length should be 10.");
214         }
215 
216         this.numMap = numMap;
217     }
218 
219     /**
220      * 10^4ごとの桁単位のマップ
221      * @param digits10Map 万、億などの桁のマップ。
222      * @throws IllegalArgumentException {@literal digits10Map size != 5}
223      */
224     public void setDigits10Map(String[] digits10Map) {
225         ArgUtils.notEmpty(digits10Map, "digits10Map");
226 
227         if(digits10Map.length != 5) {
228             throw new IllegalArgumentException("digits10Map length should be 5.");
229         }
230 
231         this.digits10Map = digits10Map;
232     }
233 
234     /**
235      * 4桁の桁単位のマップ
236      * @param digits4Map 十、百などの桁のマップ
237      * @throws IllegalArgumentException {@literal digits4Map size != 4}
238      */
239     public void setDigits4Map(String[] digits4Map) {
240 
241         ArgUtils.notEmpty(digits4Map, "digits4Map");
242 
243         if(digits4Map.length != 4) {
244             throw new IllegalArgumentException("digits4Map length should be 4.");
245         }
246 
247         this.digits4Map = digits4Map;
248     }
249 
250 }