1 package com.github.mygreen.cellformatter;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
6
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10 import com.github.mygreen.cellformatter.lang.ArgUtils;
11 import com.github.mygreen.cellformatter.lang.Utils;
12 import com.github.mygreen.cellformatter.number.FormattedNumber;
13 import com.github.mygreen.cellformatter.number.NumberFactory;
14 import com.github.mygreen.cellformatter.number.NumberPartType;
15 import com.github.mygreen.cellformatter.term.AsteriskTerm;
16 import com.github.mygreen.cellformatter.term.EscapedCharTerm;
17 import com.github.mygreen.cellformatter.term.LocaelSymbolTerm;
18 import com.github.mygreen.cellformatter.term.NumberTerm;
19 import com.github.mygreen.cellformatter.term.OtherTerm;
20 import com.github.mygreen.cellformatter.term.Term;
21 import com.github.mygreen.cellformatter.term.UnderscoreTerm;
22 import com.github.mygreen.cellformatter.term.WordTerm;
23 import com.github.mygreen.cellformatter.tokenizer.Token;
24 import com.github.mygreen.cellformatter.tokenizer.TokenStore;
25
26
27
28
29
30
31
32
33
34 public class ConditionNumberFormatterFactory extends ConditionFormatterFactory<ConditionNumberFormatter> {
35
36 private static final Logger logger = LoggerFactory.getLogger(ConditionNumberFormatterFactory.class);
37
38 private static final String[] FORMAT_CHARS = {
39 "#", "0", "?",
40 };
41
42 public static final String[] SYMBOL_CHARS = {
43 ".", ",",
44 "%", "/",
45 };
46
47 public static final String[] OTHER_CHARS = {
48 "E+", "E-",
49 "General",
50 };
51
52 public static final String[] DIGITS_START_CHARS = {
53 "1", "2", "3", "4", "5", "6", "7", "8", "9",
54 };
55
56 public static final String[] DIGITS_CHARS = {
57 "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
58 };
59
60
61
62
63 private static final String[] NUMBER_DECISTION_CHARS = toArray(
64 FORMAT_CHARS,
65 SYMBOL_CHARS
66 );
67
68
69
70
71 private static final String[] NUMBER_TERM_CHARS = toArray(
72 FORMAT_CHARS,
73 SYMBOL_CHARS,
74 OTHER_CHARS,
75 DIGITS_START_CHARS
76 );
77
78
79
80
81
82 private static final List<String> SORTED_NUMBER_TERM_CHARS = Utils.reverse(NUMBER_TERM_CHARS);
83
84
85
86
87
88
89 public boolean isNumberPattern(final TokenStore store) {
90 return store.containsAnyInFactor(NUMBER_DECISTION_CHARS);
91 }
92
93 @Override
94 public ConditionNumberFormatter create(final TokenStore store) {
95 ArgUtils.notNull(store, "store");
96
97 final ConditionNumberFormatterormatter.html#ConditionNumberFormatter">ConditionNumberFormatter formatter = new ConditionNumberFormatter(store.getConcatenatedToken());
98
99 for(Token token : store.getTokens()) {
100
101 if(token instanceof Token.Condition) {
102
103 final Token.Condition conditionToken = token.asCondition();
104 final String condition = conditionToken.getCondition();
105 formatter.addCondition(condition);
106
107 if(isConditionOperator(conditionToken)) {
108 setupConditionOperator(formatter, conditionToken);
109
110 } else if(isConditionLocale(conditionToken)) {
111 setupConditionLocale(formatter, conditionToken);
112
113 } else if(isConditionLocaleSymbol(conditionToken)) {
114 final LocaleSymbol localeSymbol = setupConditionLocaleSymbol(formatter, conditionToken);
115 formatter.addTerm(new LocaelSymbolTerm<FormattedNumber>(localeSymbol));
116
117 } else if(isConditionDbNum(conditionToken)) {
118 setupConditionDbNum(formatter, conditionToken);
119
120 } else if(isConditionColor(conditionToken)) {
121 setupConditionColor(formatter, conditionToken);
122
123 }
124
125 } else if(token instanceof Token.Word) {
126 formatter.addTerm(new WordTerm<FormattedNumber>(token.asWord()));
127
128 } else if(token instanceof Token.EscapedChar) {
129 formatter.addTerm(new EscapedCharTerm<FormattedNumber>(token.asEscapedChar()));
130
131 } else if(token instanceof Token.Underscore) {
132 formatter.addTerm(new UnderscoreTerm<FormattedNumber>(token.asUnderscore()));
133
134 } else if(token instanceof Token.Asterisk) {
135 formatter.addTerm(new AsteriskTerm<FormattedNumber>(token.asAsterisk()));
136
137 } else if(token instanceof Token.Factor) {
138
139 final List<Token> list = convertFactor(token.asFactor());
140 for(Token item : list) {
141
142 if(item instanceof Token.Formatter) {
143 if(item.getValue().equals("#")) {
144 formatter.addTerm(NumberTerm.sharp());
145
146 } else if(item.getValue().equals("0")) {
147 formatter.addTerm(NumberTerm.zero());
148
149 } else if(item.getValue().equals("?")) {
150 formatter.addTerm(NumberTerm.question());
151
152 } else {
153 logger.warn("unknown formatter : '{}'", item.getValue());
154 }
155
156 } else if(item instanceof Token.Factor) {
157 if(Utils.startsWithIgnoreCase(item.getValue(), "E")) {
158 formatter.addTerm(NumberTerm.exponnet(item));
159
160 } else if(item.getValue().equals("General")) {
161 formatter.addTerm(NumberTerm.general());
162
163 } else {
164 formatter.addTerm(new OtherTerm<FormattedNumber>(item));
165
166 }
167
168 } else if(item instanceof Token.Symbol) {
169 if(item == Token.SYMBOL_COLON) {
170 formatter.addTerm(NumberTerm.separator(item.asSymbol()));
171
172 } else {
173 formatter.addTerm(NumberTerm.symbol(item.asSymbol()));
174 }
175
176 } else if(item instanceof Token.Digits) {
177 formatter.addTerm(NumberTerm.digits(item.asDigits()));
178
179 } else {
180 formatter.addTerm(new OtherTerm<FormattedNumber>(item));
181 }
182
183 }
184
185 } else {
186 formatter.addTerm(new OtherTerm<FormattedNumber>(token));
187 }
188 }
189
190
191 setupFormat(formatter);
192
193 return formatter;
194 }
195
196 private List<Token> convertFactor(final Token.Factor factor) {
197
198 final String item = factor.getValue();
199 final int itemLength = item.length();
200
201 final List<Token> list = new ArrayList<>();
202
203 int idx = 0;
204
205 StringBuilder noTermChar = new StringBuilder();
206
207 while(idx < itemLength) {
208
209 String matchChars = null;
210 for(String chars : SORTED_NUMBER_TERM_CHARS) {
211 if(Utils.startsWithIgnoreCase(item, chars, idx)) {
212 matchChars = item.substring(idx, idx + chars.length());
213 break;
214 }
215 }
216
217 if(matchChars == null) {
218
219 noTermChar.append(item.charAt(idx));
220 idx++;
221
222 } else {
223
224
225 if(noTermChar.length() > 0) {
226
227 list.add(Token.factor(noTermChar.toString()));
228 noTermChar = new StringBuilder();
229 }
230
231 if(Utils.equalsAny(matchChars, DIGITS_START_CHARS)) {
232
233 StringBuilder digits = new StringBuilder();
234 digits.append(matchChars);
235
236 for(int i=idx+1; i < itemLength; i++) {
237 final String str = String.valueOf(item.charAt(i));
238 if(Utils.equalsAny(str, DIGITS_CHARS)) {
239 digits.append(str);
240 } else {
241 break;
242 }
243 }
244
245 list.add(Token.digits(digits.toString()));
246 idx += digits.length();
247
248 } else {
249 if(Utils.equalsAny(matchChars, FORMAT_CHARS)) {
250 list.add(Token.formatter(matchChars));
251
252 } else if(Utils.equalsAny(matchChars, SYMBOL_CHARS)) {
253 if(matchChars.equals(".")) {
254 list.add(Token.SYMBOL_DOT);
255
256 } else if(matchChars.equals(",")) {
257 list.add(Token.SYMBOL_COLON);
258
259 } else if(matchChars.equals("%")) {
260 list.add(Token.SYMBOL_PERCENT);
261
262 } else if(matchChars.equals("/")) {
263 list.add(Token.SYMBOL_SLASH);
264
265 } else {
266 logger.warn("unknown symbol : '{}'", matchChars);
267 }
268
269 } else {
270 list.add(Token.factor(matchChars));
271 }
272
273 idx += matchChars.length();
274
275 }
276
277 }
278
279 }
280
281 if(noTermChar.length() > 0) {
282 list.add(Token.factor(noTermChar.toString()));
283 }
284
285 return list;
286
287 }
288
289
290
291
292
293 private void setupFormat(final ConditionNumberFormatter formatter) {
294
295 if(formatter.containsSymbolTerm(Token.SYMBOL_SLASH)) {
296
297 setupFormatAsFraction(formatter);
298 } else {
299
300 setupFormatAsDecimal(formatter);
301 }
302
303 }
304
305 private void setupFormatAsFraction(final ConditionNumberFormatter formatter) {
306
307 final int termSize = formatter.getTerms().size();
308
309
310 int slashIndex = -1;
311 for(int i=0; i < termSize; i++) {
312
313 final Term<FormattedNumber> term = formatter.getTerms().get(i);
314 if(isSymbolTerm(term, Token.SYMBOL_SLASH)) {
315 slashIndex = i;
316 break;
317 }
318
319 }
320
321
322 boolean wholeType = false;
323 if(slashIndex > 0) {
324 NumberPartType partType = NumberPartType.Numerator;
325 int countNumeratorTerm = 0;
326 int countWholeNumberTerm = 0;
327 boolean foundFirst = false;
328
329 for(int i=slashIndex-1; i >= 0; i--) {
330 final Term<FormattedNumber> term = formatter.getTerms().get(i);
331
332 if(term instanceof NumberTerm.FormattedTerm) {
333 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
334 formattedTerm.setPart(partType);
335
336 if(partType.equals(NumberPartType.Numerator)) {
337 countNumeratorTerm++;
338 formattedTerm.setIndex(countNumeratorTerm);
339
340 } else if(partType.equals(NumberPartType.WholeNumber)) {
341 countWholeNumberTerm++;
342 formattedTerm.setIndex(countWholeNumberTerm);
343 }
344
345 if(!foundFirst) {
346 foundFirst = true;
347 }
348
349 } else {
350
351 if(foundFirst) {
352 partType = NumberPartType.WholeNumber;
353 }
354 }
355
356 }
357
358 if(countWholeNumberTerm > 0) {
359
360 wholeType = true;
361 }
362 }
363
364
365 boolean exactDenom=false;
366 int denominator = -1;
367 if(slashIndex < termSize) {
368 int countDenominatorTerm = 0;
369 int exactDenominator = -1;
370 for(int i=termSize-1; i > slashIndex; i--) {
371
372 final Term<FormattedNumber> term = formatter.getTerms().get(i);
373 if(term instanceof NumberTerm.FormattedTerm) {
374 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
375 formattedTerm.setPart(NumberPartType.Denominator);
376
377 countDenominatorTerm++;
378 formattedTerm.setIndex(countDenominatorTerm);
379
380 } else if(term instanceof NumberTerm.DigitsTerm) {
381 final NumberTerm.DigitsTerm digitsTerm = (NumberTerm.DigitsTerm) term;
382 exactDenominator = digitsTerm.getToken().intValue();
383
384 }
385 }
386
387 if(exactDenominator > 0) {
388 denominator = exactDenominator;
389 exactDenom = true;
390
391 } else if(countDenominatorTerm > 0) {
392 if(countDenominatorTerm >= 5) {
393
394 denominator = (int) Math.pow(10, 5);
395 } else {
396 denominator = (int) Math.pow(10, countDenominatorTerm);
397 }
398 } else {
399 denominator = 10;
400 }
401 }
402
403
404 boolean foundFistWholeNumber = false;
405 boolean foundFistNumerator = false;
406 boolean foundFistDenominator = false;
407 for(Term<FormattedNumber> term : formatter.getTerms()) {
408
409 if(term instanceof NumberTerm.FormattedTerm) {
410 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
411
412 if(formattedTerm.getPartType().equals(NumberPartType.WholeNumber) && !foundFistWholeNumber) {
413 formattedTerm.setLastPart(true);
414 foundFistWholeNumber = true;
415
416 } else if(formattedTerm.getPartType().equals(NumberPartType.Numerator) && !foundFistNumerator) {
417 formattedTerm.setLastPart(true);
418 foundFistNumerator = true;
419
420 } else if(formattedTerm.getPartType().equals(NumberPartType.Denominator) && !foundFistDenominator) {
421 formattedTerm.setLastPart(true);
422 foundFistDenominator = true;
423 }
424
425 }
426 }
427
428 formatter.setNumberFactory(NumberFactory.fractionNumber(denominator, exactDenom, wholeType));
429
430 }
431
432 private void setupFormatAsDecimal(final ConditionNumberFormatter formatter) {
433
434 NumberPartType partType = NumberPartType.Integer;
435 boolean foundFirst = false;
436 int countDecimalTerm = 0;
437 boolean foundPercentTerm = false;
438
439 for(Term<FormattedNumber> term : formatter.getTerms()) {
440
441 if(isSymbolTerm(term, Token.SYMBOL_PERCENT)) {
442 foundPercentTerm = true;
443
444 } else if(isSymbolTerm(term, Token.SYMBOL_DOT)) {
445 partType = NumberPartType.Decimal;
446 foundFirst = false;
447
448 } else if(term instanceof NumberTerm.ExponentTerm) {
449 partType = NumberPartType.Exponent;
450 foundFirst = false;
451
452 } else if(term instanceof NumberTerm.FormattedTerm) {
453 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
454 formattedTerm.setPart(partType);
455
456 if(partType.equals(NumberPartType.Decimal)) {
457
458 countDecimalTerm++;
459 formattedTerm.setIndex(countDecimalTerm);
460
461 } else if(!foundFirst) {
462
463 formattedTerm.setLastPart(true);
464 foundFirst = true;
465 }
466
467 }
468 }
469
470
471 foundFirst = false;
472 int countIntegerTerm = 0;
473 int countExponentTerm = 0;
474
475 final int termSize = formatter.getTerms().size();
476 for(int i=0; i < termSize; i++) {
477 final Term<FormattedNumber> term = formatter.getTerms().get(termSize-i-1);
478
479 if(term instanceof NumberTerm.FormattedTerm) {
480
481 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
482
483 if(formattedTerm.getPartType().equals(NumberPartType.Decimal)) {
484
485 if(!foundFirst) {
486 formattedTerm.setLastPart(true);
487 foundFirst = true;
488 }
489
490 } else if(formattedTerm.getPartType().equals(NumberPartType.Integer)) {
491 countIntegerTerm++;
492 formattedTerm.setIndex(countIntegerTerm);
493
494 } else if(formattedTerm.getPartType().equals(NumberPartType.Exponent)) {
495 countExponentTerm++;
496 formattedTerm.setIndex(countExponentTerm);
497 }
498
499 }
500
501 }
502
503
504 boolean useSeparator = false;
505 boolean inIntegerPater = false;
506 for(Term<FormattedNumber> term : formatter.getTerms()) {
507 if(term instanceof NumberTerm.FormattedTerm) {
508 final NumberTerm.FormattedTerm formattedTerm = (NumberTerm.FormattedTerm) term;
509
510 if(formattedTerm.getPartType().equals(NumberPartType.Integer)) {
511 if(formattedTerm.isLastPart() && formattedTerm.getIndex() > 1) {
512 inIntegerPater = true;
513 } else if(formattedTerm.getIndex() <= 1) {
514 inIntegerPater = false;
515 }
516 }
517 }
518
519 if(term instanceof NumberTerm.SeparatorTerm) {
520 if(inIntegerPater) {
521 useSeparator = true;
522 }
523
524 }
525 }
526
527
528 int indexLastFormatterdTerm = -1;
529 for(int i=0; i < termSize; i++) {
530 final Term<FormattedNumber> term = formatter.getTerms().get(termSize-i-1);
531 if(term instanceof NumberTerm.FormattedTerm) {
532 indexLastFormatterdTerm = termSize-i-1;
533 break;
534 }
535 }
536
537 int countLastColon = 0;
538 if(indexLastFormatterdTerm >= 0 && indexLastFormatterdTerm+1 < termSize) {
539
540 for(int i=indexLastFormatterdTerm+1; i < termSize; i++) {
541 final Term<FormattedNumber> term = formatter.getTerms().get(i);
542 if(term instanceof NumberTerm.SeparatorTerm) {
543 countLastColon++;
544 } else {
545 break;
546 }
547 }
548 }
549
550 if(countExponentTerm > 0) {
551
552 formatter.setNumberFactory(NumberFactory.exponentNumber(countDecimalTerm, useSeparator));
553
554 } else if(foundPercentTerm) {
555
556 formatter.setNumberFactory(NumberFactory.percentNumber(countDecimalTerm, useSeparator, countLastColon));
557
558 } else {
559
560 boolean useDBNum = false;
561 for(String condition : formatter.getConditions()) {
562 if(condition.startsWith("DBNum")) {
563 useDBNum = true;
564 break;
565 }
566 }
567
568 boolean generalFormat = false;
569 for(Term<FormattedNumber> term : formatter.getTerms()) {
570 if(term instanceof NumberTerm.GeneralTerm) {
571 generalFormat = true;
572 break;
573 }
574 }
575
576 if(useDBNum && generalFormat) {
577
578 formatter.setNumberFactory(NumberFactory.nativeNumber());
579 } else {
580 formatter.setNumberFactory(NumberFactory.decimalNumber(countDecimalTerm, useSeparator, countLastColon));
581 }
582
583 }
584
585 }
586
587 private static boolean isSymbolTerm(final Term<FormattedNumber> term, final Token.Symbol symbol) {
588
589 if(!(term instanceof NumberTerm.SymbolTerm)) {
590 return false;
591 }
592
593 final NumberTerm.SymbolTerm symbolTerm = (NumberTerm.SymbolTerm) term;
594 return symbolTerm.getToken().equals(symbol);
595
596 }
597
598 private static String[] toArray(final String[]... arrays) {
599 List<String> list = new ArrayList<>();
600 for(String[] array : arrays) {
601 list.addAll(Arrays.asList(array));
602 }
603
604 return list.toArray(new String[list.size()]);
605 }
606
607 }