View Javadoc
1   package com.github.mygreen.cellformatter;
2   
3   import java.util.regex.Matcher;
4   import java.util.regex.Pattern;
5   
6   import org.slf4j.Logger;
7   import org.slf4j.LoggerFactory;
8   
9   import com.github.mygreen.cellformatter.callback.Callback;
10  import com.github.mygreen.cellformatter.callback.DBNum1;
11  import com.github.mygreen.cellformatter.callback.DBNum2;
12  import com.github.mygreen.cellformatter.callback.DBNum3;
13  import com.github.mygreen.cellformatter.lang.MSColor;
14  import com.github.mygreen.cellformatter.lang.MSLocale;
15  import com.github.mygreen.cellformatter.tokenizer.Token;
16  import com.github.mygreen.cellformatter.tokenizer.TokenStore;
17  
18  
19  /**
20   * 条件付きの書式の組み立てるための抽象クラス。
21   * <p>主にテンプレートメソッドの実装を行う。
22   *
23   * @version 0.10
24   * @author T.TSUCHIE
25   * @param <F> 組み立てるフォーマッタクラス
26   */
27  public abstract class ConditionFormatterFactory<F> {
28  
29      private static Logger logger = LoggerFactory.getLogger(ConditionFormatterFactory.class);
30  
31      /**
32       * 条件付き書式を組み立てる
33       * @param store
34       * @return
35       */
36      public abstract F create(TokenStore store);
37  
38      /**
39       * 演算子を使用した条件式のパターン
40       */
41      private static final Pattern PATTERN_CONDITION_OPERATOR = Pattern.compile("\\[([><=]+)([+-]*[0-9\\.]+)\\]");
42  
43      /**
44       * ロケールの条件式のパターン
45       */
46      private static final Pattern PATTERN_CONDITION_LOCALE = Pattern.compile("\\[\\$\\-([0-9a-zA-Z]+)\\]");
47  
48      /**
49       * 記号付きロケールの条件式のパターン
50       */
51      private static final Pattern PATTERN_CONDITION_LOCALE_SYMBOL = Pattern.compile("\\[\\$(.+)\\-([0-9a-zA-Z]+)\\]");
52  
53      /**
54       * 特殊な処理の条件式のパターン
55       */
56      private static final Pattern PATTERN_CONDITION_DBNUM = Pattern.compile("\\[DBNum([0-9]+)\\]");
57  
58      /**
59       * インデックス形式の色の条件式のパターン
60       */
61      private static final Pattern PATTERN_CONDITION_INDEX_COLOR = Pattern.compile("\\[(色|Color)([0-9]+)\\]");
62  
63      /**
64       * {@literal '[<=1000]'}などの演算子の条件式かどうか。
65       * @param token 判定対象のトークン。
66       * @return true: 演算子の条件式。
67       *
68       */
69      protected boolean isConditionOperator(final Token.Condition token) {
70          return PATTERN_CONDITION_OPERATOR.matcher(token.getValue()).matches();
71      }
72  
73      /**
74       * {@literal '[$-403]'}などのロケールの条件式かどうか。
75       * @param token 判定対象のトークン。
76       * @return true: ロケールの条件式。
77       *
78       */
79      protected boolean isConditionLocale(final Token.Condition token) {
80          return PATTERN_CONDITION_LOCALE.matcher(token.getValue()).matches();
81      }
82  
83      /**
84       * {@literal '[$€-403]'}などの記号付きロケールの条件式かどうか。
85       * @since 0.8
86       * @param token 判定対象のトークン。
87       * @return true: 記号付きロケールの条件式。     */
88      protected boolean isConditionLocaleSymbol(final Token.Condition token) {
89          return PATTERN_CONDITION_LOCALE_SYMBOL.matcher(token.getValue()).matches();
90      }
91  
92      /**
93       * {@literal '[DBNum1]'}などの組み込み処理の条件式かどうか。
94       * @param token 判定対象のトークン。
95       * @return true: 特殊な処理の条件式。
96       *
97       */
98      protected boolean isConditionDbNum(final Token.Condition token) {
99          return PATTERN_CONDITION_DBNUM.matcher(token.getValue()).matches();
100     }
101 
102     /**
103      * {@literal '[Red]'}などの色の条件式の書式かどうか
104      * @param token 判定対象のトークン。
105      * @return true: 色の条件式。
106      *
107      */
108     protected boolean isConditionColor(final Token.Condition token) {
109         if(MSColor.valueOfKnownColor(token.getCondition()) != null) {
110             return true;
111         }
112         return PATTERN_CONDITION_INDEX_COLOR.matcher(token.getValue()).matches();
113     }
114 
115     /**
116      * {@literal '[<=1000]'}などの数値の条件を組み立てる
117      * @param formatter 現在の組み立て中のフォーマッタのインスタンス。
118      * @param token 条件式のトークン。
119      * @return 演算子の条件式。
120      * @throws IllegalArgumentException 処理対象の条件として一致しない場合
121      */
122     protected ConditionOperator setupConditionOperator(final ConditionFormatter formatter, final Token.Condition token) {
123 
124         final Matcher matcher = PATTERN_CONDITION_OPERATOR.matcher(token.getValue());
125         if(!matcher.matches()) {
126             throw new IllegalArgumentException("not match condition:" + token.getValue());
127         }
128 
129         final String operator = matcher.group(1);
130         final String number = matcher.group(2);
131         final double condition = Double.valueOf(number);
132 
133         final ConditionOperator conditionOperator;
134         switch(operator) {
135             case "=":
136                 conditionOperator = new ConditionOperator.Equal(condition);
137                 break;
138             case "<>":
139                 conditionOperator = new ConditionOperator.NotEqual(condition);
140                 break;
141             case ">":
142                 conditionOperator = new ConditionOperator.GreaterThan(condition);
143                 break;
144             case "<":
145                 conditionOperator = new ConditionOperator.LessThan(condition);
146                 break;
147             case ">=":
148                 conditionOperator = new ConditionOperator.GreaterEqual(condition);
149                 break;
150             case "<=":
151                 conditionOperator = new ConditionOperator.LessEqual(condition);
152                 break;
153             default:
154                 logger.warn("unknown operator : {}", operator);
155                 conditionOperator = ConditionOperator.ALL;
156                 break;
157 
158         }
159 
160         formatter.setOperator(conditionOperator);
161         return conditionOperator;
162     }
163 
164     /**
165      * {@literal '[$-403]'}などのロケールの条件を組み立てる
166      * @param formatter 現在の組み立て中のフォーマッタのインスタンス。
167      * @param token 条件式のトークン。
168      * @return ロケールの条件式。
169      * @throws IllegalArgumentException 処理対象の条件として一致しない場合
170      */
171     protected MSLocale setupConditionLocale(final ConditionFormatter formatter, final Token.Condition token) {
172 
173         final Matcher matcher = PATTERN_CONDITION_LOCALE.matcher(token.getValue());
174         if(!matcher.matches()) {
175             throw new IllegalArgumentException("not match condition:" + token.getValue());
176         }
177 
178         final String number = matcher.group(1);
179 
180         // 16進数=>10進数に直す
181         final int value = Integer.valueOf(number, 16);
182         MSLocale locale = MSLocale.createKnownLocale(value);
183         if(locale == null) {
184             locale = new MSLocale(value);
185         }
186 
187         formatter.setLocale(locale);
188 
189         return locale;
190 
191     }
192 
193     /**
194      * {@literal '[$€-403]'}などの記号付きロケールの条件を組み立てる
195      * @since 0.8
196      * @param formatter 現在の組み立て中のフォーマッタのインスタンス。
197      * @param token 条件式のトークン。
198      * @return 記号付きロケールの条件式。
199      * @throws IllegalArgumentException 処理対象の条件として一致しない場合
200      */
201     protected LocaleSymbol setupConditionLocaleSymbol(final ConditionFormatter formatter, final Token.Condition token) {
202 
203         final Matcher matcher = PATTERN_CONDITION_LOCALE_SYMBOL.matcher(token.getValue());
204         if(!matcher.matches()) {
205             throw new IllegalArgumentException("not match condition:" + token.getValue());
206         }
207 
208         final String symbol = matcher.group(1);
209         final String number = matcher.group(2);
210 
211         // 16進数=>10進数に直す
212         final int value = Integer.valueOf(number, 16);
213         MSLocale locale = MSLocale.createKnownLocale(value);
214         if(locale == null) {
215             locale = new MSLocale(value);
216         }
217 
218         formatter.setLocale(locale);
219 
220         return new LocaleSymbol(locale, symbol);
221 
222     }
223 
224     /**
225      * {@literal '[DBNum1]'}などの組み込み処理の条件を組み立てる。
226      * @param formatter 現在の組み立て中のフォーマッタのインスタンス。
227      * @param token 条件式のトークン。
228      * @return 組み込みの条件式。
229      * @throws IllegalArgumentException 処理対象の条件として一致しない場合
230      */
231     protected Callback<?> setupConditionDbNum(final ConditionFormatter formatter, final Token.Condition token) {
232 
233         final Matcher matcher = PATTERN_CONDITION_DBNUM.matcher(token.getValue());
234         if(!matcher.matches()) {
235             throw new IllegalArgumentException("not match condition:" + token.getValue());
236         }
237 
238         Callback<?> callback = null;
239         final String number = matcher.group(1);
240         if(number.startsWith("1")) {
241             callback = DBNum1.create();
242 
243         } else if(number.startsWith("2")) {
244             callback = DBNum2.create();
245 
246         } else if(number.startsWith("3")) {
247             callback = DBNum3.create();
248         }
249 
250         if(callback != null) {
251             formatter.addCallback(callback);
252         }
253 
254         return callback;
255     }
256 
257 
258 
259     /**
260      * {@literal '[Red]'}などの色の条件の組み立てる。
261      * @param formatter 現在の組み立て中のフォーマッタのインスタンス。
262      * @param token 条件式のトークン。
263      * @return 色の条件式。
264      * @throws IllegalArgumentException 処理対象の条件として一致しない場合
265      */
266     protected MSColor setupConditionColor(final ConditionFormatter formatter, final Token.Condition token) {
267 
268         // 名前指定の場合
269         MSColor color = MSColor.valueOfKnownColor(token.getCondition());
270         if(color != null) {
271             formatter.setColor(color);
272             return color;
273         }
274 
275         // インデックス指定の場合
276         final Matcher matcher = PATTERN_CONDITION_INDEX_COLOR.matcher(token.getValue());
277         if(!matcher.matches()) {
278             throw new IllegalArgumentException("not match condition:" + token.getValue());
279         }
280 
281 //        final String prefix = matcher.group(1);
282         final String number = matcher.group(2);
283         final short index = Short.valueOf(number);
284         color = MSColor.valueOfIndexColor(index);
285 
286         formatter.setColor(color);
287 
288         return color;
289     }
290 
291 }