From b953cc391328af937917ed94de6aeafb83cc715c Mon Sep 17 00:00:00 2001 From: Damyan Ivanov Date: Sat, 13 Feb 2021 17:36:50 +0200 Subject: [PATCH] show pattern match results in template editor --- .../model/TemplateDetailsItem.java | 100 +++++++++++++++--- .../TemplateDetailSourceSelectorFragment.java | 5 +- .../ui/templates/TemplateDetailsAdapter.java | 68 +++++++----- .../templates/TemplateDetailsViewModel.java | 1 + .../res/layout/template_details_header.xml | 26 ++++- app/src/main/res/values-bg/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 162 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java index 1f3211c4..822f7438 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/TemplateDetailsItem.java @@ -18,6 +18,13 @@ 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; @@ -30,6 +37,8 @@ import net.ktnx.mobileledger.utils.Misc; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -393,9 +402,9 @@ abstract public class TemplateDetailsItem { 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 transactionDescription = PossiblyMatchedValue.withLiteralString(""); private PossiblyMatchedValue transactionComment = @@ -403,6 +412,7 @@ abstract public class TemplateDetailsItem { private PossiblyMatchedValue dateYear = PossiblyMatchedValue.withLiteralInt(null); private PossiblyMatchedValue dateMonth = PossiblyMatchedValue.withLiteralInt(null); private PossiblyMatchedValue dateDay = PossiblyMatchedValue.withLiteralInt(null); + private SpannableString testMatch; private Header() { super(Type.HEADER); } @@ -411,6 +421,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); @@ -420,6 +431,11 @@ abstract public class TemplateDetailsItem { dateMonth = new PossiblyMatchedValue<>(origin.dateMonth); dateDay = new PossiblyMatchedValue<>(origin.dateDay); } + 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 String getName() { return name; } @@ -431,18 +447,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 +472,8 @@ abstract public class TemplateDetailsItem { } public void setTestText(String testText) { this.testText = testText; + + checkPatternMatch(); } public String getTransactionDescription() { return transactionDescription.getValue(); @@ -551,7 +568,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); } public String getMatchGroupText(int group) { if (compiledPattern != null && testText != null) { @@ -623,5 +642,60 @@ abstract public class TemplateDetailsItem { 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()) { + ArrayList notMatched = new ArrayList<>(); + 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() - 1, + 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; + } } } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java index 1bb48404..97c88785 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/TemplateDetailSourceSelectorFragment.java @@ -106,7 +106,7 @@ public class TemplateDetailSourceSelectorFragment extends AppCompatDialogFragmen Logger.debug("templates", String.format("Trying to match pattern '%s' against text '%s'", patternText, testText)); - if (matcher.matches()) { + if (matcher.find()) { if (matcher.groupCount() >= 0) { ArrayList list = new ArrayList<>(); for (short g = 1; g <= matcher.groupCount(); g++) { @@ -159,7 +159,8 @@ public class TemplateDetailSourceSelectorFragment extends AppCompatDialogFragmen } else { b.list.setVisibility(View.GONE); - b.templateError.setText(mPatternProblem); + b.templateError.setText( + (mPatternProblem != 0) ? mPatternProblem : R.string.pattern_without_groups); b.templateError.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java index 46bdba55..e472049b 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateDetailsAdapter.java @@ -196,6 +196,7 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR)); - b.templateDetailsYearSource.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR)); + b.templateDetailsYearSourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DATE_YEAR)); + b.templateDetailsYearSource.setOnClickListener(v -> selectHeaderDetailSource(v, + HeaderDetail.DATE_YEAR)); if (header.hasLiteralDateMonth()) { b.templateDetailsMonthSource.setText(R.string.template_details_source_literal); @@ -362,16 +368,15 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH)); - b.templateDetailsMonthSource.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.DATE_MONTH)); + b.templateDetailsMonthSourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, + HeaderDetail.DATE_MONTH)); + b.templateDetailsMonthSource.setOnClickListener(v -> selectHeaderDetailSource(v, + HeaderDetail.DATE_MONTH)); if (header.hasLiteralDateDay()) { b.templateDetailsDaySource.setText(R.string.template_details_source_literal); final Integer dateDay = header.getDateDay(); - b.templateDetailsDateDay.setText( - (dateDay == null) ? null : String.valueOf(dateDay)); + b.templateDetailsDateDay.setText((dateDay == null) ? null : String.valueOf(dateDay)); b.templateDetailsDateDayLayout.setVisibility(View.VISIBLE); } else { @@ -380,14 +385,11 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); - b.templateDetailsDaySource.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); + b.templateDetailsDaySourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); + b.templateDetailsDaySource.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DATE_DAY)); if (header.hasLiteralTransactionDescription()) { - b.templateTransactionDescriptionSource.setText( - R.string.template_details_source_literal); + b.templateTransactionDescriptionSource.setText(R.string.template_details_source_literal); b.transactionDescription.setText(header.getTransactionDescription()); b.transactionDescriptionLayout.setVisibility(View.VISIBLE); } @@ -395,18 +397,14 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); - b.templateTransactionDescriptionSource.setOnClickListener( - v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); + b.templateTransactionDescriptionSourceLabel.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); + b.templateTransactionDescriptionSource.setOnClickListener(v -> selectHeaderDetailSource(v, HeaderDetail.DESCRIPTION)); if (header.hasLiteralTransactionComment()) { - b.templateTransactionCommentSource.setText( - R.string.template_details_source_literal); + b.templateTransactionCommentSource.setText(R.string.template_details_source_literal); b.transactionComment.setText(header.getTransactionComment()); b.transactionCommentLayout.setVisibility(View.VISIBLE); } @@ -424,6 +422,28 @@ class TemplateDetailsAdapter extends RecyclerView.Adapter(Collections.emptyList()); private Long mPatternId; private String mDefaultPatternName; + public String getDefaultPatternName() { return mDefaultPatternName; } diff --git a/app/src/main/res/layout/template_details_header.xml b/app/src/main/res/layout/template_details_header.xml index d16ce4eb..58b5535f 100644 --- a/app/src/main/res/layout/template_details_header.xml +++ b/app/src/main/res/layout/template_details_header.xml @@ -43,6 +43,8 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:textAppearance="?attr/textAppearanceListItem" + app:endIconMode="clear_text" + app:layout_constraintBottom_toTopOf="@id/pattern_hint_title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/pattern_name_layout" @@ -55,6 +57,28 @@ android:inputType="text" /> + + Източник на името на сметката Макетът „%s“ е изтрит Връщане + Резултат от прилагането на шаблона diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e126f28..fd13e066 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -227,4 +227,5 @@ New template Template \'%s\' deleted Undo + Pattern match result -- 2.39.2