]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java
add fallback flag to templates
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / model / TemplateDetailsItem.java
index 1f3211c4c797de00091ada0403b0a31b0bcb3c49..2eb3346df313e6c9ae040e84e05aa594d1856064 100644 (file)
 package net.ktnx.mobileledger.model;
 
 import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
 
 import androidx.annotation.NonNull;
 
@@ -25,11 +32,14 @@ import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.db.TemplateAccount;
 import net.ktnx.mobileledger.db.TemplateBase;
 import net.ktnx.mobileledger.db.TemplateHeader;
+import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
 
 import org.jetbrains.annotations.Contract;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.Locale;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -37,7 +47,7 @@ import java.util.regex.PatternSyntaxException;
 abstract public class TemplateDetailsItem {
     private final Type type;
     protected Long id;
-    protected Long position;
+    protected long position;
 
     protected TemplateDetailsItem(Type type) {
         this.type = type;
@@ -88,12 +98,15 @@ abstract public class TemplateDetailsItem {
             else
                 header.setDateYearMatchGroup(ph.getDateYearMatchGroup());
 
+            header.setFallback(ph.isFallback());
+
             return header;
         }
         else if (p instanceof TemplateAccount) {
             TemplateAccount pa = (TemplateAccount) p;
             AccountRow acc = createAccountRow();
             acc.setId(pa.getId());
+            acc.setPosition(pa.getPosition());
 
             if (pa.getAccountNameMatchGroup() == null)
                 acc.setAccountName(Misc.nullIsEmpty(pa.getAccountName()));
@@ -163,7 +176,7 @@ abstract public class TemplateDetailsItem {
     public long getPosition() {
         return position;
     }
-    public void setPosition(Long position) {
+    public void setPosition(long position) {
         this.position = position;
     }
     abstract public String getProblem(@NonNull Resources r, int patternGroupCount);
@@ -217,6 +230,11 @@ abstract public class TemplateDetailsItem {
             result.setValue(initialValue);
             return result;
         }
+        public void copyFrom(@NonNull PossiblyMatchedValue<T> origin) {
+            literalValue = origin.literalValue;
+            value = origin.value;
+            matchGroup = origin.matchGroup;
+        }
         public T getValue() {
             if (!literalValue)
                 throw new IllegalStateException("Value is not literal");
@@ -262,6 +280,12 @@ abstract public class TemplateDetailsItem {
                 return "grp:" + matchGroup;
             return "<null>";
         }
+        public boolean isEmpty() {
+            if (literalValue)
+                return value == null || Misc.emptyIsNull(value.toString()) == null;
+
+            return matchGroup > 0;
+        }
     }
 
     public static class TYPE {
@@ -278,8 +302,18 @@ abstract public class TemplateDetailsItem {
                 PossiblyMatchedValue.withLiteralFloat(null);
         private final PossiblyMatchedValue<Currency> currency = new PossiblyMatchedValue<>();
         private boolean negateAmount;
-        private AccountRow() {
+        public AccountRow() {
+            super(Type.ACCOUNT_ITEM);
+        }
+        public AccountRow(AccountRow origin) {
             super(Type.ACCOUNT_ITEM);
+            id = origin.id;
+            position = origin.position;
+            accountName.copyFrom(origin.accountName);
+            accountComment.copyFrom(origin.accountComment);
+            amount.copyFrom(origin.amount);
+            currency.copyFrom(origin.currency);
+            negateAmount = origin.negateAmount;
         }
         public boolean isNegateAmount() {
             return negateAmount;
@@ -352,8 +386,15 @@ abstract public class TemplateDetailsItem {
             return accountComment.hasLiteralValue();
         }
         public boolean equalContents(AccountRow o) {
+            if (position != o.position) {
+                Logger.debug("cmpAcc",
+                        String.format(Locale.US, "[%d] != [%d]: pos %d != pos %d", getId(),
+                                o.getId(), position, o.position));
+                return false;
+            }
             return amount.equals(o.amount) && accountName.equals(o.accountName) &&
-                   accountComment.equals(o.accountComment) && negateAmount == o.negateAmount;
+                   position == o.position && accountComment.equals(o.accountComment) &&
+                   negateAmount == o.negateAmount;
         }
         public void switchToLiteralAmount() {
             amount.switchToLiteral();
@@ -388,14 +429,17 @@ abstract public class TemplateDetailsItem {
 
             return result;
         }
+        public boolean isEmpty() {
+            return accountName.isEmpty() && accountComment.isEmpty() && amount.isEmpty();
+        }
     }
 
     public static class Header extends TemplateDetailsItem {
         private String pattern = "";
         private String testText = "";
+        private String name = "";
         private Pattern compiledPattern;
         private String patternError;
-        private String name = "";
         private PossiblyMatchedValue<String> transactionDescription =
                 PossiblyMatchedValue.withLiteralString("");
         private PossiblyMatchedValue<String> transactionComment =
@@ -403,6 +447,8 @@ abstract public class TemplateDetailsItem {
         private PossiblyMatchedValue<Integer> dateYear = PossiblyMatchedValue.withLiteralInt(null);
         private PossiblyMatchedValue<Integer> dateMonth = PossiblyMatchedValue.withLiteralInt(null);
         private PossiblyMatchedValue<Integer> dateDay = PossiblyMatchedValue.withLiteralInt(null);
+        private SpannableString testMatch;
+        private boolean isFallback;
         private Header() {
             super(Type.HEADER);
         }
@@ -411,6 +457,7 @@ abstract public class TemplateDetailsItem {
             id = origin.id;
             name = origin.name;
             testText = origin.testText;
+            testMatch = origin.testMatch;
             setPattern(origin.pattern);
 
             transactionDescription = new PossiblyMatchedValue<>(origin.transactionDescription);
@@ -419,6 +466,19 @@ abstract public class TemplateDetailsItem {
             dateYear = new PossiblyMatchedValue<>(origin.dateYear);
             dateMonth = new PossiblyMatchedValue<>(origin.dateMonth);
             dateDay = new PossiblyMatchedValue<>(origin.dateDay);
+
+            isFallback = origin.isFallback;
+        }
+        private static StyleSpan capturedSpan() { return new StyleSpan(Typeface.BOLD); }
+        private static UnderlineSpan matchedSpan() { return new UnderlineSpan(); }
+        private static ForegroundColorSpan notMatchedSpan() {
+            return new ForegroundColorSpan(Color.GRAY);
+        }
+        public boolean isFallback() {
+            return isFallback;
+        }
+        public void setFallback(boolean fallback) {
+            this.isFallback = fallback;
         }
         public String getName() {
             return name;
@@ -431,18 +491,17 @@ abstract public class TemplateDetailsItem {
         }
         public void setPattern(String pattern) {
             this.pattern = pattern;
-            if (pattern != null) {
-                try {
-                    this.compiledPattern = Pattern.compile(pattern);
-                    this.patternError = null;
-                }
-                catch (PatternSyntaxException e) {
-                    this.compiledPattern = null;
-                    this.patternError = e.getMessage();
-                }
+            try {
+                this.compiledPattern = Pattern.compile(pattern);
+                checkPatternMatch();
             }
-            else {
-                patternError = "Missing pattern";
+            catch (PatternSyntaxException ex) {
+                patternError = ex.getDescription();
+                compiledPattern = null;
+
+                testMatch = new SpannableString(testText);
+                testMatch.setSpan(notMatchedSpan(), 0, testText.length() - 1,
+                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             }
         }
         @NonNull
@@ -457,6 +516,8 @@ abstract public class TemplateDetailsItem {
         }
         public void setTestText(String testText) {
             this.testText = testText;
+
+            checkPatternMatch();
         }
         public String getTransactionDescription() {
             return transactionDescription.getValue();
@@ -551,7 +612,9 @@ abstract public class TemplateDetailsItem {
                 return true;
 
             return Misc.equalStrings(name, o.name) && Misc.equalStrings(pattern, o.pattern) &&
-                   Misc.equalStrings(testText, o.testText);
+                   Misc.equalStrings(testText, o.testText) &&
+                   Misc.equalStrings(patternError, o.patternError) &&
+                   Objects.equals(testMatch, o.testMatch) && isFallback == o.isFallback;
         }
         public String getMatchGroupText(int group) {
             if (compiledPattern != null && testText != null) {
@@ -621,7 +684,63 @@ abstract public class TemplateDetailsItem {
             else
                 result.setDateDayMatchGroup(dateDay.getMatchGroup());
 
+            result.setFallback(isFallback);
+
             return result;
         }
+        public SpannableString getTestMatch() {
+            return testMatch;
+        }
+        public void checkPatternMatch() {
+            patternError = null;
+            testMatch = null;
+
+            if (pattern != null) {
+                try {
+                    if (Misc.emptyIsNull(testText) != null) {
+                        SpannableString ss = new SpannableString(testText);
+                        Matcher m = compiledPattern.matcher(testText);
+                        if (m.find()) {
+                            if (m.start() > 0)
+                                ss.setSpan(notMatchedSpan(), 0, m.start(),
+                                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                            if (m.end() < testText.length() - 1)
+                                ss.setSpan(notMatchedSpan(), m.end(), testText.length(),
+                                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+                            ss.setSpan(matchedSpan(), m.start(0), m.end(0),
+                                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+                            if (m.groupCount() > 0) {
+                                for (int g = 1; g <= m.groupCount(); g++) {
+                                    ss.setSpan(capturedSpan(), m.start(g), m.end(g),
+                                            Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                                }
+                            }
+                        }
+                        else {
+                            patternError = "Pattern does not match";
+                            ss.setSpan(new ForegroundColorSpan(Color.GRAY), 0,
+                                    testText.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                        }
+
+                        testMatch = ss;
+                    }
+                }
+                catch (PatternSyntaxException e) {
+                    this.compiledPattern = null;
+                    this.patternError = e.getMessage();
+                }
+            }
+            else {
+                patternError = "Missing pattern";
+            }
+        }
+        public String getPatternError() {
+            return patternError;
+        }
+        public SpannableString testMatch() {
+            return testMatch;
+        }
     }
 }