]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java
Room-based profile management
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / model / TemplateDetailsItem.java
1 /*
2  * Copyright © 2021 Damyan Ivanov.
3  * This file is part of MoLe.
4  * MoLe is free software: you can distribute it and/or modify it
5  * under the term of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your opinion), any later version.
8  *
9  * MoLe is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License terms for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
16  */
17
18 package net.ktnx.mobileledger.model;
19
20 import android.content.res.Resources;
21 import android.graphics.Color;
22 import android.graphics.Typeface;
23 import android.text.SpannableString;
24 import android.text.Spanned;
25 import android.text.style.ForegroundColorSpan;
26 import android.text.style.StyleSpan;
27 import android.text.style.UnderlineSpan;
28
29 import androidx.annotation.NonNull;
30
31 import net.ktnx.mobileledger.R;
32 import net.ktnx.mobileledger.db.TemplateAccount;
33 import net.ktnx.mobileledger.db.TemplateBase;
34 import net.ktnx.mobileledger.db.TemplateHeader;
35 import net.ktnx.mobileledger.utils.Logger;
36 import net.ktnx.mobileledger.utils.Misc;
37
38 import org.jetbrains.annotations.Contract;
39 import org.jetbrains.annotations.NotNull;
40
41 import java.util.Locale;
42 import java.util.Objects;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45 import java.util.regex.PatternSyntaxException;
46
47 abstract public class TemplateDetailsItem {
48     private final Type type;
49     protected Long id;
50     protected long position;
51
52     protected TemplateDetailsItem(Type type) {
53         this.type = type;
54     }
55     @Contract(" -> new")
56     public static @NotNull TemplateDetailsItem.Header createHeader() {
57         return new Header();
58     }
59     public static @NotNull TemplateDetailsItem.Header createHeader(Header origin) {
60         return new Header(origin);
61     }
62     @Contract("-> new")
63     public static @NotNull TemplateDetailsItem.AccountRow createAccountRow() {
64         return new AccountRow();
65     }
66     public static TemplateDetailsItem fromRoomObject(TemplateBase p) {
67         if (p instanceof TemplateHeader) {
68             TemplateHeader ph = (TemplateHeader) p;
69             Header header = createHeader();
70             header.setId(ph.getId());
71             header.setName(ph.getName());
72             header.setPattern(ph.getRegularExpression());
73             header.setTestText(ph.getTestText());
74
75             if (ph.getTransactionDescriptionMatchGroup() == null)
76                 header.setTransactionDescription(ph.getTransactionDescription());
77             else
78                 header.setTransactionDescriptionMatchGroup(
79                         ph.getTransactionDescriptionMatchGroup());
80
81             if (ph.getTransactionCommentMatchGroup() == null)
82                 header.setTransactionComment(ph.getTransactionComment());
83             else
84                 header.setTransactionCommentMatchGroup(ph.getTransactionCommentMatchGroup());
85
86             if (ph.getDateDayMatchGroup() == null)
87                 header.setDateDay(ph.getDateDay());
88             else
89                 header.setDateDayMatchGroup(ph.getDateDayMatchGroup());
90
91             if (ph.getDateMonthMatchGroup() == null)
92                 header.setDateMonth(ph.getDateMonth());
93             else
94                 header.setDateMonthMatchGroup(ph.getDateMonthMatchGroup());
95
96             if (ph.getDateYearMatchGroup() == null)
97                 header.setDateYear(ph.getDateYear());
98             else
99                 header.setDateYearMatchGroup(ph.getDateYearMatchGroup());
100
101             header.setFallback(ph.isFallback());
102
103             return header;
104         }
105         else if (p instanceof TemplateAccount) {
106             TemplateAccount pa = (TemplateAccount) p;
107             AccountRow acc = createAccountRow();
108             acc.setId(pa.getId());
109             acc.setPosition(pa.getPosition());
110
111             if (pa.getAccountNameMatchGroup() == null)
112                 acc.setAccountName(Misc.nullIsEmpty(pa.getAccountName()));
113             else
114                 acc.setAccountNameMatchGroup(pa.getAccountNameMatchGroup());
115
116             if (pa.getAccountCommentMatchGroup() == null)
117                 acc.setAccountComment(Misc.nullIsEmpty(pa.getAccountComment()));
118             else
119                 acc.setAccountCommentMatchGroup(pa.getAccountCommentMatchGroup());
120
121             if (pa.getCurrencyMatchGroup() == null) {
122                 acc.setCurrency(pa.getCurrencyObject());
123             }
124             else
125                 acc.setCurrencyMatchGroup(pa.getCurrencyMatchGroup());
126
127             final Integer amountMatchGroup = pa.getAmountMatchGroup();
128             if (amountMatchGroup != null && amountMatchGroup > 0) {
129                 acc.setAmountMatchGroup(amountMatchGroup);
130                 final Boolean negateAmount = pa.getNegateAmount();
131                 acc.setNegateAmount(negateAmount != null && negateAmount);
132             }
133             else
134                 acc.setAmount(pa.getAmount());
135
136             return acc;
137         }
138         else {
139             throw new IllegalStateException("Unexpected item class " + p.getClass());
140         }
141     }
142     public Header asHeaderItem() {
143         ensureType(Type.HEADER);
144         return (Header) this;
145     }
146     public AccountRow asAccountRowItem() {
147         ensureType(Type.ACCOUNT_ITEM);
148         return (AccountRow) this;
149     }
150     private void ensureType(Type type) {
151         if (this.type != type)
152             throw new IllegalStateException(
153                     String.format("Type is %s, but %s is required", this.type.toString(),
154                             type.toString()));
155     }
156     void ensureTrue(boolean flag) {
157         if (!flag)
158             throw new IllegalStateException(
159                     "Literal value requested, but it is matched via a pattern group");
160     }
161     void ensureFalse(boolean flag) {
162         if (flag)
163             throw new IllegalStateException("Matching group requested, but the value is a literal");
164     }
165     public long getId() {
166         return id;
167     }
168     public void setId(Long id) {
169         this.id = id;
170     }
171     public void setId(int id) {
172         this.id = (long) id;
173     }
174     public long getPosition() {
175         return position;
176     }
177     public void setPosition(long position) {
178         this.position = position;
179     }
180     abstract public String getProblem(@NonNull Resources r, int patternGroupCount);
181     public Type getType() {
182         return type;
183     }
184     public enum Type {
185         HEADER(TYPE.header), ACCOUNT_ITEM(TYPE.accountItem);
186         final int index;
187         Type(int i) {
188             index = i;
189         }
190         public int toInt() {
191             return index;
192         }
193     }
194
195     static class PossiblyMatchedValue<T> {
196         private boolean literalValue;
197         private T value;
198         private int matchGroup;
199         public PossiblyMatchedValue() {
200             literalValue = true;
201             value = null;
202         }
203         public PossiblyMatchedValue(@NonNull PossiblyMatchedValue<T> origin) {
204             literalValue = origin.literalValue;
205             value = origin.value;
206             matchGroup = origin.matchGroup;
207         }
208         @NonNull
209         public static PossiblyMatchedValue<Integer> withLiteralInt(Integer initialValue) {
210             PossiblyMatchedValue<Integer> result = new PossiblyMatchedValue<>();
211             result.setValue(initialValue);
212             return result;
213         }
214         @NonNull
215         public static PossiblyMatchedValue<Float> withLiteralFloat(Float initialValue) {
216             PossiblyMatchedValue<Float> result = new PossiblyMatchedValue<>();
217             result.setValue(initialValue);
218             return result;
219         }
220         public static PossiblyMatchedValue<Short> withLiteralShort(Short initialValue) {
221             PossiblyMatchedValue<Short> result = new PossiblyMatchedValue<>();
222             result.setValue(initialValue);
223             return result;
224         }
225         @NonNull
226         public static PossiblyMatchedValue<String> withLiteralString(String initialValue) {
227             PossiblyMatchedValue<String> result = new PossiblyMatchedValue<>();
228             result.setValue(initialValue);
229             return result;
230         }
231         public void copyFrom(@NonNull PossiblyMatchedValue<T> origin) {
232             literalValue = origin.literalValue;
233             value = origin.value;
234             matchGroup = origin.matchGroup;
235         }
236         public T getValue() {
237             if (!literalValue)
238                 throw new IllegalStateException("Value is not literal");
239             return value;
240         }
241         public void setValue(T newValue) {
242             value = newValue;
243             literalValue = true;
244         }
245         public boolean hasLiteralValue() {
246             return literalValue;
247         }
248         public int getMatchGroup() {
249             if (literalValue)
250                 throw new IllegalStateException("Value is literal");
251             return matchGroup;
252         }
253         public void setMatchGroup(int group) {
254             this.matchGroup = group;
255             literalValue = false;
256         }
257         public boolean equals(PossiblyMatchedValue<T> other) {
258             if (!other.literalValue == literalValue)
259                 return false;
260             if (literalValue) {
261                 if (value == null)
262                     return other.value == null;
263                 return value.equals(other.value);
264             }
265             else
266                 return matchGroup == other.matchGroup;
267         }
268         public void switchToLiteral() {
269             literalValue = true;
270         }
271         public String toString() {
272             if (literalValue)
273                 if (value == null)
274                     return "<null>";
275                 else
276                     return value.toString();
277             if (matchGroup > 0)
278                 return "grp:" + matchGroup;
279             return "<null>";
280         }
281         public boolean isEmpty() {
282             if (literalValue)
283                 return value == null || Misc.emptyIsNull(value.toString()) == null;
284
285             return matchGroup > 0;
286         }
287     }
288
289     public static class TYPE {
290         public static final int header = 0;
291         public static final int accountItem = 1;
292     }
293
294     public static class AccountRow extends TemplateDetailsItem {
295         private final PossiblyMatchedValue<String> accountName =
296                 PossiblyMatchedValue.withLiteralString("");
297         private final PossiblyMatchedValue<String> accountComment =
298                 PossiblyMatchedValue.withLiteralString("");
299         private final PossiblyMatchedValue<Float> amount =
300                 PossiblyMatchedValue.withLiteralFloat(null);
301         private final PossiblyMatchedValue<net.ktnx.mobileledger.db.Currency> currency =
302                 new PossiblyMatchedValue<>();
303         private boolean negateAmount;
304         public AccountRow() {
305             super(Type.ACCOUNT_ITEM);
306         }
307         public AccountRow(AccountRow origin) {
308             super(Type.ACCOUNT_ITEM);
309             id = origin.id;
310             position = origin.position;
311             accountName.copyFrom(origin.accountName);
312             accountComment.copyFrom(origin.accountComment);
313             amount.copyFrom(origin.amount);
314             currency.copyFrom(origin.currency);
315             negateAmount = origin.negateAmount;
316         }
317         public boolean isNegateAmount() {
318             return negateAmount;
319         }
320         public void setNegateAmount(boolean negateAmount) {
321             this.negateAmount = negateAmount;
322         }
323         public int getAccountCommentMatchGroup() {
324             return accountComment.getMatchGroup();
325         }
326         public void setAccountCommentMatchGroup(int group) {
327             accountComment.setMatchGroup(group);
328         }
329         public String getAccountComment() {
330             return accountComment.getValue();
331         }
332         public void setAccountComment(String comment) {
333             this.accountComment.setValue(comment);
334         }
335         public int getCurrencyMatchGroup() {
336             return currency.getMatchGroup();
337         }
338         public void setCurrencyMatchGroup(int group) {
339             currency.setMatchGroup(group);
340         }
341         public net.ktnx.mobileledger.db.Currency getCurrency() {
342             return currency.getValue();
343         }
344         public void setCurrency(net.ktnx.mobileledger.db.Currency currency) {
345             this.currency.setValue(currency);
346         }
347         public int getAccountNameMatchGroup() {
348             return accountName.getMatchGroup();
349         }
350         public void setAccountNameMatchGroup(int group) {
351             accountName.setMatchGroup(group);
352         }
353         public String getAccountName() {
354             return accountName.getValue();
355         }
356         public void setAccountName(String accountName) {
357             this.accountName.setValue(accountName);
358         }
359         public boolean hasLiteralAccountName() { return accountName.hasLiteralValue(); }
360         public boolean hasLiteralAmount() {
361             return amount.hasLiteralValue();
362         }
363         public int getAmountMatchGroup() {
364             return amount.getMatchGroup();
365         }
366         public void setAmountMatchGroup(int group) {
367             amount.setMatchGroup(group);
368         }
369         public Float getAmount() {
370             return amount.getValue();
371         }
372         public void setAmount(Float amount) {
373             this.amount.setValue(amount);
374         }
375         public String getProblem(@NonNull Resources r, int patternGroupCount) {
376             if (Misc.emptyIsNull(accountName.getValue()) == null)
377                 return r.getString(R.string.account_name_is_empty);
378             if (!amount.hasLiteralValue() &&
379                 (amount.getMatchGroup() < 1 || amount.getMatchGroup() > patternGroupCount))
380                 return r.getString(R.string.invalid_matching_group_number);
381
382             return null;
383         }
384         public boolean hasLiteralAccountComment() {
385             return accountComment.hasLiteralValue();
386         }
387         public boolean equalContents(AccountRow o) {
388             if (position != o.position) {
389                 Logger.debug("cmpAcc",
390                         String.format(Locale.US, "[%d] != [%d]: pos %d != pos %d", getId(),
391                                 o.getId(), position, o.position));
392                 return false;
393             }
394             return amount.equals(o.amount) && accountName.equals(o.accountName) &&
395                    position == o.position && accountComment.equals(o.accountComment) &&
396                    negateAmount == o.negateAmount;
397         }
398         public void switchToLiteralAmount() {
399             amount.switchToLiteral();
400         }
401         public void switchToLiteralAccountName() {
402             accountName.switchToLiteral();
403         }
404         public void switchToLiteralAccountComment() {
405             accountComment.switchToLiteral();
406         }
407         public TemplateAccount toDBO(@NonNull Long patternId) {
408             TemplateAccount result = new TemplateAccount(id, patternId, position);
409
410             if (accountName.hasLiteralValue())
411                 result.setAccountName(accountName.getValue());
412             else
413                 result.setAccountNameMatchGroup(accountName.getMatchGroup());
414
415             if (accountComment.hasLiteralValue())
416                 result.setAccountComment(accountComment.getValue());
417             else
418                 result.setAccountCommentMatchGroup(accountComment.getMatchGroup());
419
420             if (amount.hasLiteralValue()) {
421                 result.setAmount(amount.getValue());
422                 result.setNegateAmount(null);
423             }
424             else {
425                 result.setAmountMatchGroup(amount.getMatchGroup());
426                 result.setNegateAmount(negateAmount ? true : null);
427             }
428
429             return result;
430         }
431         public boolean isEmpty() {
432             return accountName.isEmpty() && accountComment.isEmpty() && amount.isEmpty();
433         }
434     }
435
436     public static class Header extends TemplateDetailsItem {
437         private String pattern = "";
438         private String testText = "";
439         private String name = "";
440         private Pattern compiledPattern;
441         private String patternError;
442         private PossiblyMatchedValue<String> transactionDescription =
443                 PossiblyMatchedValue.withLiteralString("");
444         private PossiblyMatchedValue<String> transactionComment =
445                 PossiblyMatchedValue.withLiteralString("");
446         private PossiblyMatchedValue<Integer> dateYear = PossiblyMatchedValue.withLiteralInt(null);
447         private PossiblyMatchedValue<Integer> dateMonth = PossiblyMatchedValue.withLiteralInt(null);
448         private PossiblyMatchedValue<Integer> dateDay = PossiblyMatchedValue.withLiteralInt(null);
449         private SpannableString testMatch;
450         private boolean isFallback;
451         private Header() {
452             super(Type.HEADER);
453         }
454         public Header(Header origin) {
455             this();
456             id = origin.id;
457             name = origin.name;
458             testText = origin.testText;
459             testMatch = origin.testMatch;
460             setPattern(origin.pattern);
461
462             transactionDescription = new PossiblyMatchedValue<>(origin.transactionDescription);
463             transactionComment = new PossiblyMatchedValue<>(origin.transactionComment);
464
465             dateYear = new PossiblyMatchedValue<>(origin.dateYear);
466             dateMonth = new PossiblyMatchedValue<>(origin.dateMonth);
467             dateDay = new PossiblyMatchedValue<>(origin.dateDay);
468
469             isFallback = origin.isFallback;
470         }
471         private static StyleSpan capturedSpan() { return new StyleSpan(Typeface.BOLD); }
472         private static UnderlineSpan matchedSpan() { return new UnderlineSpan(); }
473         private static ForegroundColorSpan notMatchedSpan() {
474             return new ForegroundColorSpan(Color.GRAY);
475         }
476         public boolean isFallback() {
477             return isFallback;
478         }
479         public void setFallback(boolean fallback) {
480             this.isFallback = fallback;
481         }
482         public String getName() {
483             return name;
484         }
485         public void setName(String name) {
486             this.name = name;
487         }
488         public String getPattern() {
489             return pattern;
490         }
491         public void setPattern(String pattern) {
492             this.pattern = pattern;
493             try {
494                 this.compiledPattern = Pattern.compile(pattern);
495                 checkPatternMatch();
496             }
497             catch (PatternSyntaxException ex) {
498                 patternError = ex.getDescription();
499                 compiledPattern = null;
500
501                 testMatch = new SpannableString(testText);
502                 if (!testText.isEmpty())
503                     testMatch.setSpan(notMatchedSpan(), 0, testText.length() - 1,
504                             Spanned.SPAN_INCLUSIVE_INCLUSIVE);
505             }
506         }
507         @NonNull
508         @Override
509         public String toString() {
510             return super.toString() +
511                    String.format(" name[%s] pat[%s] test[%s] tran[%s] com[%s]", name, pattern,
512                            testText, transactionDescription, transactionComment);
513         }
514         public String getTestText() {
515             return testText;
516         }
517         public void setTestText(String testText) {
518             this.testText = testText;
519
520             checkPatternMatch();
521         }
522         public String getTransactionDescription() {
523             return transactionDescription.getValue();
524         }
525         public void setTransactionDescription(String transactionDescription) {
526             this.transactionDescription.setValue(transactionDescription);
527         }
528         public String getTransactionComment() {
529             return transactionComment.getValue();
530         }
531         public void setTransactionComment(String transactionComment) {
532             this.transactionComment.setValue(transactionComment);
533         }
534         public Integer getDateYear() {
535             return dateYear.getValue();
536         }
537         public void setDateYear(Integer dateYear) {
538             this.dateYear.setValue(dateYear);
539         }
540         public Integer getDateMonth() {
541             return dateMonth.getValue();
542         }
543         public void setDateMonth(Integer dateMonth) {
544             this.dateMonth.setValue(dateMonth);
545         }
546         public Integer getDateDay() {
547             return dateDay.getValue();
548         }
549         public void setDateDay(Integer dateDay) {
550             this.dateDay.setValue(dateDay);
551         }
552         public int getDateYearMatchGroup() {
553             return dateYear.getMatchGroup();
554         }
555         public void setDateYearMatchGroup(int dateYearMatchGroup) {
556             this.dateYear.setMatchGroup(dateYearMatchGroup);
557         }
558         public int getDateMonthMatchGroup() {
559             return dateMonth.getMatchGroup();
560         }
561         public void setDateMonthMatchGroup(int dateMonthMatchGroup) {
562             this.dateMonth.setMatchGroup(dateMonthMatchGroup);
563         }
564         public int getDateDayMatchGroup() {
565             return dateDay.getMatchGroup();
566         }
567         public void setDateDayMatchGroup(int dateDayMatchGroup) {
568             this.dateDay.setMatchGroup(dateDayMatchGroup);
569         }
570         public boolean hasLiteralDateYear() {
571             return dateYear.hasLiteralValue();
572         }
573         public boolean hasLiteralDateMonth() {
574             return dateMonth.hasLiteralValue();
575         }
576         public boolean hasLiteralDateDay() {
577             return dateDay.hasLiteralValue();
578         }
579         public boolean hasLiteralTransactionDescription() { return transactionDescription.hasLiteralValue(); }
580         public boolean hasLiteralTransactionComment() { return transactionComment.hasLiteralValue(); }
581         public String getProblem(@NonNull Resources r, int patternGroupCount) {
582             if (patternError != null)
583                 return r.getString(R.string.pattern_has_errors) + ": " + patternError;
584             if (Misc.emptyIsNull(pattern) == null)
585                 return r.getString(R.string.pattern_is_empty);
586
587             if (!dateYear.hasLiteralValue() && compiledPattern != null &&
588                 (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
589                 return r.getString(R.string.invalid_matching_group_number);
590
591             if (!dateMonth.hasLiteralValue() && compiledPattern != null &&
592                 (dateMonth.getMatchGroup() < 1 || dateMonth.getMatchGroup() > patternGroupCount))
593                 return r.getString(R.string.invalid_matching_group_number);
594
595             if (!dateDay.hasLiteralValue() && compiledPattern != null &&
596                 (dateDay.getMatchGroup() < 1 || dateDay.getMatchGroup() > patternGroupCount))
597                 return r.getString(R.string.invalid_matching_group_number);
598
599             return null;
600         }
601
602         public boolean equalContents(Header o) {
603             if (!dateDay.equals(o.dateDay))
604                 return false;
605             if (!dateMonth.equals(o.dateMonth))
606                 return false;
607             if (!dateYear.equals(o.dateYear))
608                 return false;
609             if (!transactionDescription.equals(o.transactionDescription))
610                 return false;
611             if (!transactionComment.equals(o.transactionComment))
612                 return true;
613
614             return Misc.equalStrings(name, o.name) && Misc.equalStrings(pattern, o.pattern) &&
615                    Misc.equalStrings(testText, o.testText) &&
616                    Misc.equalStrings(patternError, o.patternError) &&
617                    Objects.equals(testMatch, o.testMatch) && isFallback == o.isFallback;
618         }
619         public String getMatchGroupText(int group) {
620             if (compiledPattern != null && testText != null) {
621                 Matcher m = compiledPattern.matcher(testText);
622                 if (m.matches())
623                     return m.group(group);
624             }
625
626             return "ø";
627         }
628         public Pattern getCompiledPattern() {
629             return compiledPattern;
630         }
631         public void switchToLiteralTransactionDescription() {
632             transactionDescription.switchToLiteral();
633         }
634         public void switchToLiteralTransactionComment() {
635             transactionComment.switchToLiteral();
636         }
637         public int getTransactionDescriptionMatchGroup() {
638             return transactionDescription.getMatchGroup();
639         }
640         public void setTransactionDescriptionMatchGroup(int group) {
641             transactionDescription.setMatchGroup(group);
642         }
643         public int getTransactionCommentMatchGroup() {
644             return transactionComment.getMatchGroup();
645         }
646         public void setTransactionCommentMatchGroup(int group) {
647             transactionComment.setMatchGroup(group);
648         }
649         public void switchToLiteralDateYear() {
650             dateYear.switchToLiteral();
651         }
652         public void switchToLiteralDateMonth() {
653             dateMonth.switchToLiteral();
654         }
655         public void switchToLiteralDateDay() { dateDay.switchToLiteral(); }
656         public TemplateHeader toDBO() {
657             TemplateHeader result = new TemplateHeader(id, name, pattern);
658
659             if (Misc.emptyIsNull(testText) != null)
660                 result.setTestText(testText);
661
662             if (transactionDescription.hasLiteralValue())
663                 result.setTransactionDescription(transactionDescription.getValue());
664             else
665                 result.setTransactionDescriptionMatchGroup(transactionDescription.getMatchGroup());
666
667             if (transactionComment.hasLiteralValue())
668                 result.setTransactionComment(transactionComment.getValue());
669             else
670                 result.setTransactionCommentMatchGroup(transactionComment.getMatchGroup());
671
672             if (dateYear.hasLiteralValue())
673                 result.setDateYear(dateYear.getValue());
674             else
675                 result.setDateYearMatchGroup(dateYear.getMatchGroup());
676
677             if (dateMonth.hasLiteralValue())
678                 result.setDateMonth(dateMonth.getValue());
679             else
680                 result.setDateMonthMatchGroup(dateMonth.getMatchGroup());
681
682             if (dateDay.hasLiteralValue())
683                 result.setDateDay(dateDay.getValue());
684             else
685                 result.setDateDayMatchGroup(dateDay.getMatchGroup());
686
687             result.setFallback(isFallback);
688
689             return result;
690         }
691         public SpannableString getTestMatch() {
692             return testMatch;
693         }
694         public void checkPatternMatch() {
695             patternError = null;
696             testMatch = null;
697
698             if (pattern != null) {
699                 try {
700                     if (Misc.emptyIsNull(testText) != null) {
701                         SpannableString ss = new SpannableString(testText);
702                         Matcher m = compiledPattern.matcher(testText);
703                         if (m.find()) {
704                             if (m.start() > 0)
705                                 ss.setSpan(notMatchedSpan(), 0, m.start(),
706                                         Spanned.SPAN_INCLUSIVE_INCLUSIVE);
707                             if (m.end() < testText.length() - 1)
708                                 ss.setSpan(notMatchedSpan(), m.end(), testText.length(),
709                                         Spanned.SPAN_INCLUSIVE_INCLUSIVE);
710
711                             ss.setSpan(matchedSpan(), m.start(0), m.end(0),
712                                     Spanned.SPAN_INCLUSIVE_INCLUSIVE);
713
714                             if (m.groupCount() > 0) {
715                                 for (int g = 1; g <= m.groupCount(); g++) {
716                                     ss.setSpan(capturedSpan(), m.start(g), m.end(g),
717                                             Spanned.SPAN_INCLUSIVE_INCLUSIVE);
718                                 }
719                             }
720                         }
721                         else {
722                             patternError = "Pattern does not match";
723                             ss.setSpan(new ForegroundColorSpan(Color.GRAY), 0,
724                                     testText.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
725                         }
726
727                         testMatch = ss;
728                     }
729                 }
730                 catch (PatternSyntaxException e) {
731                     this.compiledPattern = null;
732                     this.patternError = e.getMessage();
733                 }
734             }
735             else {
736                 patternError = "Missing pattern";
737             }
738         }
739         public String getPatternError() {
740             return patternError;
741         }
742         public SpannableString testMatch() {
743             return testMatch;
744         }
745     }
746 }