That fuck shit the fascists are using
1package org.tm.archive.util;
2
3import android.text.Annotation;
4import android.text.Spannable;
5import android.text.SpannableString;
6import android.text.TextUtils;
7import android.text.style.CharacterStyle;
8
9import androidx.annotation.NonNull;
10import androidx.annotation.Nullable;
11
12import com.annimon.stream.Stream;
13
14import org.signal.core.util.StringUtil;
15import org.signal.libsignal.protocol.util.Pair;
16import org.tm.archive.components.spoiler.SpoilerAnnotation;
17
18import java.security.InvalidParameterException;
19import java.util.Collections;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Locale;
23
24public class SearchUtil {
25
26 public static final int STRICT = 0;
27 public static final int MATCH_ALL = 1;
28
29 public static Spannable getHighlightedSpan(@NonNull Locale locale,
30 @NonNull StyleFactory styleFactory,
31 @Nullable CharSequence text,
32 @Nullable String highlight,
33 int matchMode)
34 {
35 if (TextUtils.isEmpty(text)) {
36 return new SpannableString("");
37 }
38
39 text = StringUtil.replace(text, '\n', " ");
40
41 return getHighlightedSpan(locale, styleFactory, SpannableString.valueOf(text), highlight, matchMode);
42 }
43
44 public static Spannable getHighlightedSpan(@NonNull Locale locale,
45 @NonNull StyleFactory styleFactory,
46 @Nullable Spannable text,
47 @Nullable String highlight,
48 int matchMode)
49 {
50 if (TextUtils.isEmpty(text)) {
51 return new SpannableString("");
52 }
53
54
55 if (TextUtils.isEmpty(highlight)) {
56 return text;
57 }
58
59 SpannableString spanned = new SpannableString(text);
60 List<Pair<Integer, Integer>> ranges;
61
62 switch (matchMode) {
63 case STRICT:
64 ranges = getStrictHighlightRanges(locale, text.toString(), highlight);
65 break;
66 case MATCH_ALL:
67 ranges = getHighlightRanges(locale, text.toString(), highlight);
68 break;
69 default:
70 throw new InvalidParameterException("match mode must be STRICT or MATCH_ALL: " + matchMode);
71 }
72
73 for (Pair<Integer, Integer> range : ranges) {
74 CharacterStyle[] styles = styleFactory.createStyles();
75 for (CharacterStyle style : styles) {
76 List<Annotation> annotations = SpoilerAnnotation.getSpoilerAnnotations(spanned, range.first(), range.second());
77 if (annotations.isEmpty()) {
78 spanned.setSpan(style, range.first(), range.second(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
79 }
80 }
81 }
82
83 return spanned;
84 }
85
86 static List<Pair<Integer, Integer>> getStrictHighlightRanges(@NonNull Locale locale,
87 @NonNull String text,
88 @NonNull String highlight)
89 {
90 if (text.length() == 0) {
91 return Collections.emptyList();
92 }
93
94 String normalizedText = text.toLowerCase(locale);
95 String normalizedHighlight = highlight.toLowerCase(locale);
96 List<String> highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList();
97
98 List<Pair<Integer, Integer>> ranges = new LinkedList<>();
99
100 int lastHighlightEndIndex = 0;
101
102 for (String highlightToken : highlightTokens) {
103 int index;
104
105 do {
106 index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex);
107 lastHighlightEndIndex = index + highlightToken.length();
108 } while (index > 0 && !Character.isWhitespace(normalizedText.charAt(index - 1)));
109
110 if (index >= 0) {
111 ranges.add(new Pair<>(index, lastHighlightEndIndex));
112 }
113
114 if (index < 0 || lastHighlightEndIndex >= normalizedText.length()) {
115 break;
116 }
117 }
118
119 if (ranges.size() != highlightTokens.size()) {
120 return Collections.emptyList();
121 }
122
123 return ranges;
124 }
125
126 static List<Pair<Integer, Integer>> getHighlightRanges(@NonNull Locale locale,
127 @NonNull String text,
128 @NonNull String highlight)
129 {
130 if (text.length() == 0) {
131 return Collections.emptyList();
132 }
133
134 String normalizedText = text.toLowerCase(locale);
135 String normalizedHighlight = highlight.toLowerCase(locale);
136 List<String> highlightTokens = Stream.of(normalizedHighlight.split("\\s")).filter(s -> s.trim().length() > 0).toList();
137
138 List<Pair<Integer, Integer>> ranges = new LinkedList<>();
139
140 int lastHighlightEndIndex = 0;
141
142 for (String highlightToken : highlightTokens) {
143 int index = 0;
144 lastHighlightEndIndex = 0;
145
146 while (index != -1) {
147 index = normalizedText.indexOf(highlightToken, lastHighlightEndIndex);
148 if (index != -1) {
149 lastHighlightEndIndex = index + highlightToken.length();
150 ranges.add(new Pair<>(index, lastHighlightEndIndex));
151 index = lastHighlightEndIndex;
152 }
153 }
154 }
155
156 return ranges;
157 }
158
159 public interface StyleFactory {
160 CharacterStyle[] createStyles();
161 }
162}