View Javadoc
1   package com.github.mygreen.supercsv.cellprocessor.conversion;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Comparator;
6   import java.util.HashMap;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import com.github.mygreen.supercsv.util.ArgUtils;
13  import com.github.mygreen.supercsv.util.Utils;
14  
15  /**
16   * 文字を置換するクラス
17   * 
18   * @version 2.0.1
19   * @since 2.0
20   * @author T.TSUCHIE
21   *
22   */
23  public class CharReplacer {
24      
25      /** 置換元が1文字の場合 */
26      private final Map<Character, String> singles = new HashMap<>();
27      
28      /** 置換元が2文字以上の文字の場合 */
29      private final List<MultiChar> multi = new ArrayList<>();
30      
31      /**
32       * 置換元の文字が2文字以上の場合
33       *
34       */
35      private static class MultiChar {
36          
37          private final String word;
38          
39          private final String replacement;
40          
41          private MultiChar(final String word, final String replacement) {
42              this.word = word;
43              this.replacement = replacement;
44          }
45          
46      }
47      
48      /**
49       * 置換対象の文字を登録する。
50       * @param word 置換対象の文字
51       * @param replacement 置換後の文字
52       * @throws IllegalArgumentException word is empty.
53       * @throws NullPointerException replacement is null.
54       */
55      public void register(final String word, final String replacement) {
56          ArgUtils.notEmpty(word, "word");
57          ArgUtils.notNull(replacement, "replacement");
58          
59          if(word.length() == 1) {
60              singles.computeIfAbsent(word.charAt(0), key -> replacement);
61              
62          } else {
63              multi.add(new MultiChar(word, replacement));
64          }
65          
66      }
67      
68      /**
69       * 登録後に置換文字の整理を行うため、必ず実行すること。
70       */
71      public void ready() {
72          
73          // 複数の文字の場合、重複を排除する。
74          final Set<String> duplicatedWords = new HashSet<>();
75          final List<MultiChar> newMulti = new ArrayList<>();
76          for(MultiChar word : multi) {
77              if(duplicatedWords.contains(word.word)) {
78                  continue;
79              }
80              duplicatedWords.add(word.word);
81              newMulti.add(word);
82          }
83          
84          // 複数の文字の場合、文字長が長い順に並び替える。
85          Collections.sort(newMulti, new Comparator<MultiChar>() {
86              
87              @Override
88              public int compare(final MultiChar o1, final MultiChar o2) {
89                  
90                  final int length1 = o1.word.length();
91                  final int length2 = o2.word.length();
92                  if(length1 < length2) {
93                      return 1;
94                      
95                  } else if(length1 > length2) {
96                      return -1;
97                      
98                  } else {
99                      return o1.word.compareTo(o2.word);
100                 }
101             }
102         });
103         
104         multi.clear();
105         multi.addAll(newMulti);
106         
107     }
108     
109     /**
110      * 登録された文字を元に置換する。
111      * @param text 置換対象の文字
112      * @return 置換した文字。置換対象の文字がnullまたは空文字の場合、置換しない。
113      */
114     public String replace(final String text) {
115         
116         if(Utils.isEmpty(text)) {
117             return text;
118         }
119         
120         final int length = text.length();
121         StringBuilder replaced = new StringBuilder();
122         int index = 0;
123         
124         while(index < length) {
125             int multiIndex = replaceMulti(text, index, replaced);
126             if(multiIndex > index) {
127                 index = multiIndex;
128                 continue;
129             }
130             
131             int singleIndex = replaceSingle(text, index, replaced);
132             if(singleIndex > index) {
133                 index = singleIndex;
134                 continue;
135             }
136             
137             // 置換できるものがない場合
138             replaced.append(text.charAt(index));
139             index++;
140             
141         }
142         
143         return replaced.toString();
144     }
145     
146     private int replaceSingle(final String source, final int index, final StringBuilder replaced) {
147         
148         final char c = source.charAt(index);
149         if(singles.containsKey(c)) {
150             replaced.append(singles.get(c));
151             return index + 1;
152         }
153         
154         return index;
155         
156     }
157     
158     private int replaceMulti(final String source, final int index, final StringBuilder replaced) {
159         
160         for(MultiChar word : multi) {
161             if(source.indexOf(word.word, index) == index) {
162                 replaced.append(word.replacement);
163                 return index + word.word.length();
164             }
165         }
166         
167         return index;
168     }
169     
170 }