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;
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;
abstract public class TemplateDetailsItem {
private final Type type;
protected Long id;
- protected Long position;
+ protected long position;
protected TemplateDetailsItem(Type type) {
this.type = type;
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()));
acc.setAccountCommentMatchGroup(pa.getAccountCommentMatchGroup());
if (pa.getCurrencyMatchGroup() == null) {
- final Integer currencyId = pa.getCurrency();
- if (currencyId != null && currencyId > 0)
- acc.setCurrency(Currency.loadById(currencyId));
+ acc.setCurrency(pa.getCurrencyObject());
}
else
acc.setCurrencyMatchGroup(pa.getCurrencyMatchGroup());
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);
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");
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 {
private final PossiblyMatchedValue<String> accountComment =
PossiblyMatchedValue.withLiteralString("");
private final PossiblyMatchedValue<Float> amount =
- PossiblyMatchedValue.withLiteralFloat(0f);
- private final PossiblyMatchedValue<Currency> currency = new PossiblyMatchedValue<>();
+ PossiblyMatchedValue.withLiteralFloat(null);
+ private final PossiblyMatchedValue<net.ktnx.mobileledger.db.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;
public void setCurrencyMatchGroup(int group) {
currency.setMatchGroup(group);
}
- public Currency getCurrency() {
+ public net.ktnx.mobileledger.db.Currency getCurrency() {
return currency.getValue();
}
- public void setCurrency(Currency currency) {
+ public void setCurrency(net.ktnx.mobileledger.db.Currency currency) {
this.currency.setValue(currency);
}
public int getAccountNameMatchGroup() {
public boolean hasLiteralAccountComment() {
return accountComment.hasLiteralValue();
}
+ public boolean hasLiteralCurrency() { return currency.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();
}
+ public void switchToLiteralCurrency() {
+ currency.switchToLiteral();
+ }
public void switchToLiteralAccountName() {
accountName.switchToLiteral();
}
result.setNegateAmount(negateAmount ? true : null);
}
+ if (currency.hasLiteralValue()) {
+ net.ktnx.mobileledger.db.Currency c = currency.getValue();
+ result.setCurrency((c == null) ? null : c.getId());
+ }
+ else {
+ result.setCurrencyMatchGroup(currency.getMatchGroup());
+ }
+
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 =
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);
}
id = origin.id;
name = origin.name;
testText = origin.testText;
+ testMatch = origin.testMatch;
setPattern(origin.pattern);
transactionDescription = new PossiblyMatchedValue<>(origin.transactionDescription);
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;
}
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);
+ if (!testText.isEmpty())
+ testMatch.setSpan(notMatchedSpan(), 0, testText.length() - 1,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
}
@NonNull
}
public void setTestText(String testText) {
this.testText = testText;
+
+ checkPatternMatch();
}
public String getTransactionDescription() {
return transactionDescription.getValue();
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) {
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;
+ }
}
}