Skip to content

Commit d57d3d8

Browse files
committed
API to disable span support
1 parent 43f4cc2 commit d57d3d8

3 files changed

Lines changed: 182 additions & 16 deletions

File tree

src/main/java/com/squareup/phrase/Phrase.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import android.content.res.Resources;
2121
import android.support.annotation.PluralsRes;
2222
import android.support.annotation.StringRes;
23+
import android.text.Editable;
2324
import android.text.SpannableStringBuilder;
2425
import android.view.View;
2526
import android.widget.TextView;
@@ -54,8 +55,8 @@ public final class Phrase {
5455
private final CharSequence pattern;
5556

5657
/** All keys parsed from the original pattern, sans braces. */
57-
private final Set<String> keys = new HashSet<String>();
58-
private final Map<String, CharSequence> keysToValues = new HashMap<String, CharSequence>();
58+
private final Set<String> keys = new HashSet<>();
59+
private final Map<String, CharSequence> keysToValues = new HashMap<>();
5960

6061
/** Cached result after replacing all keys with corresponding values. */
6162
private CharSequence formatted;
@@ -67,6 +68,8 @@ public final class Phrase {
6768
private char curChar;
6869
private int curCharIndex;
6970

71+
private boolean spanSupportedEnabled = true;
72+
7073
/** Indicates parsing is complete. */
7174
private static final int EOF = 0;
7275

@@ -75,6 +78,7 @@ public final class Phrase {
7578
*
7679
* @throws IllegalArgumentException if pattern contains any syntax errors.
7780
*/
81+
@SuppressWarnings("UnusedDeclaration") // Public API.
7882
public static Phrase from(Fragment f, @StringRes int patternResourceId) {
7983
return from(f.getResources(), patternResourceId);
8084
}
@@ -84,6 +88,7 @@ public static Phrase from(Fragment f, @StringRes int patternResourceId) {
8488
*
8589
* @throws IllegalArgumentException if pattern contains any syntax errors.
8690
*/
91+
@SuppressWarnings("UnusedDeclaration") // Public API.
8792
public static Phrase from(View v, @StringRes int patternResourceId) {
8893
return from(v.getResources(), patternResourceId);
8994
}
@@ -93,6 +98,7 @@ public static Phrase from(View v, @StringRes int patternResourceId) {
9398
*
9499
* @throws IllegalArgumentException if pattern contains any syntax errors.
95100
*/
101+
@SuppressWarnings("UnusedDeclaration") // Public API.
96102
public static Phrase from(Context c, @StringRes int patternResourceId) {
97103
return from(c.getResources(), patternResourceId);
98104
}
@@ -102,6 +108,7 @@ public static Phrase from(Context c, @StringRes int patternResourceId) {
102108
*
103109
* @throws IllegalArgumentException if pattern contains any syntax errors.
104110
*/
111+
@SuppressWarnings("UnusedDeclaration") // Public API.
105112
public static Phrase from(Resources r, @StringRes int patternResourceId) {
106113
return from(r.getText(patternResourceId));
107114
}
@@ -111,6 +118,7 @@ public static Phrase from(Resources r, @StringRes int patternResourceId) {
111118
*
112119
* @throws IllegalArgumentException if pattern contains any syntax errors.
113120
*/
121+
@SuppressWarnings("UnusedDeclaration") // Public API.
114122
public static Phrase fromPlural(View v, @PluralsRes int patternResourceId, int quantity) {
115123
return fromPlural(v.getResources(), patternResourceId, quantity);
116124
}
@@ -120,6 +128,7 @@ public static Phrase fromPlural(View v, @PluralsRes int patternResourceId, int q
120128
*
121129
* @throws IllegalArgumentException if pattern contains any syntax errors.
122130
*/
131+
@SuppressWarnings("UnusedDeclaration") // Public API.
123132
public static Phrase fromPlural(Context c, @PluralsRes int patternResourceId, int quantity) {
124133
return fromPlural(c.getResources(), patternResourceId, quantity);
125134
}
@@ -129,6 +138,7 @@ public static Phrase fromPlural(Context c, @PluralsRes int patternResourceId, in
129138
*
130139
* @throws IllegalArgumentException if pattern contains any syntax errors.
131140
*/
141+
@SuppressWarnings("UnusedDeclaration") // Public API.
132142
public static Phrase fromPlural(Resources r, @PluralsRes int patternResourceId, int quantity) {
133143
return from(r.getQuantityText(patternResourceId, quantity));
134144
}
@@ -138,6 +148,7 @@ public static Phrase fromPlural(Resources r, @PluralsRes int patternResourceId,
138148
*
139149
* @throws IllegalArgumentException if pattern contains any syntax errors.
140150
*/
151+
@SuppressWarnings("UnusedDeclaration") // Public API.
141152
public static Phrase from(CharSequence pattern) {
142153
return new Phrase(pattern);
143154
}
@@ -148,6 +159,7 @@ public static Phrase from(CharSequence pattern) {
148159
*
149160
* @throws IllegalArgumentException if the key is not in the pattern.
150161
*/
162+
@SuppressWarnings("UnusedDeclaration") // Public API.
151163
public Phrase put(String key, CharSequence value) {
152164
if (!keys.contains(key)) {
153165
throw new IllegalArgumentException("Invalid key: " + key);
@@ -167,6 +179,7 @@ public Phrase put(String key, CharSequence value) {
167179
*
168180
* @see #put(String, CharSequence)
169181
*/
182+
@SuppressWarnings("UnusedDeclaration") // Public API.
170183
public Phrase put(String key, int value) {
171184
return put(key, Integer.toString(value));
172185
}
@@ -176,6 +189,7 @@ public Phrase put(String key, int value) {
176189
*
177190
* @see #put(String, CharSequence)
178191
*/
192+
@SuppressWarnings("UnusedDeclaration") // Public API.
179193
public Phrase putOptional(String key, CharSequence value) {
180194
return keys.contains(key) ? put(key, value) : this;
181195
}
@@ -186,6 +200,7 @@ public Phrase putOptional(String key, CharSequence value) {
186200
*
187201
* @see #putOptional(String, CharSequence)
188202
*/
203+
@SuppressWarnings("UnusedDeclaration") // Public API.
189204
public Phrase putOptional(String key, int value) {
190205
return keys.contains(key) ? put(key, value) : this;
191206
}
@@ -195,16 +210,17 @@ public Phrase putOptional(String key, int value) {
195210
*
196211
* @throws IllegalArgumentException if any keys are not replaced.
197212
*/
213+
@SuppressWarnings("UnusedDeclaration") // Public API.
198214
public CharSequence format() {
199215
if (formatted == null) {
200216
if (!keysToValues.keySet().containsAll(keys)) {
201-
Set<String> missingKeys = new HashSet<String>(keys);
217+
Set<String> missingKeys = new HashSet<>(keys);
202218
missingKeys.removeAll(keysToValues.keySet());
203219
throw new IllegalArgumentException("Missing keys: " + missingKeys);
204220
}
205221

206222
// Copy the original pattern to preserve all spans, such as bold, italic, etc.
207-
SpannableStringBuilder sb = new SpannableStringBuilder(pattern);
223+
Editable sb = createEditable(pattern);
208224
for (Token t = head; t != null; t = t.next) {
209225
t.expand(sb, keysToValues);
210226
}
@@ -214,6 +230,16 @@ public CharSequence format() {
214230
return formatted;
215231
}
216232

233+
/**
234+
* This can be useful for unit tests that don't want to use {@link SpannableStringBuilder}
235+
* which can cause a stub exception or be no-op.
236+
* Default is <code>true</code>.
237+
*/
238+
@SuppressWarnings("UnusedDeclaration") // Public API.
239+
public void setSpanSupportEnabled(boolean spanSupportedEnabled) {
240+
this.spanSupportedEnabled = spanSupportedEnabled;
241+
}
242+
217243
/** "Formats and sets as text in textView." */
218244
public void into(TextView textView) {
219245
if (textView == null) {
@@ -230,6 +256,13 @@ public void into(TextView textView) {
230256
return pattern.toString();
231257
}
232258

259+
private Editable createEditable(CharSequence pattern) {
260+
if (spanSupportedEnabled) {
261+
return new SpannableStringBuilder(pattern);
262+
}
263+
return new SimpleEditable(pattern);
264+
}
265+
233266
private Phrase(CharSequence pattern) {
234267
curChar = (pattern.length() > 0) ? pattern.charAt(0) : EOF;
235268

@@ -335,7 +368,7 @@ protected Token(Token prev) {
335368
}
336369

337370
/** Replace text in {@code target} with this token's associated value. */
338-
abstract void expand(SpannableStringBuilder target, Map<String, CharSequence> data);
371+
abstract void expand(Editable target, Map<String, CharSequence> data);
339372

340373
/** Returns the number of characters after expansion. */
341374
abstract int getFormattedLength();
@@ -361,7 +394,7 @@ private static class TextToken extends Token {
361394
this.textLength = textLength;
362395
}
363396

364-
@Override void expand(SpannableStringBuilder target, Map<String, CharSequence> data) {
397+
@Override void expand(Editable target, Map<String, CharSequence> data) {
365398
// Don't alter spans in the target.
366399
}
367400

@@ -376,7 +409,7 @@ private static class LeftCurlyBracketToken extends Token {
376409
super(prev);
377410
}
378411

379-
@Override void expand(SpannableStringBuilder target, Map<String, CharSequence> data) {
412+
@Override void expand(Editable target, Map<String, CharSequence> data) {
380413
int start = getFormattedStart();
381414
target.replace(start, start + 2, "{");
382415
}
@@ -398,7 +431,7 @@ private static class KeyToken extends Token {
398431
this.key = key;
399432
}
400433

401-
@Override void expand(SpannableStringBuilder target, Map<String, CharSequence> data) {
434+
@Override void expand(Editable target, Map<String, CharSequence> data) {
402435
value = data.get(key);
403436

404437
int replaceFrom = getFormattedStart();
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.squareup.phrase;
2+
3+
import android.support.annotation.NonNull;
4+
import android.text.Editable;
5+
import android.text.InputFilter;
6+
7+
import static java.util.regex.Matcher.quoteReplacement;
8+
import static java.util.regex.Pattern.quote;
9+
10+
/**
11+
* Provides basic support for replacing tokens with values without spans. This
12+
* is used when {@link Phrase#setSpanSupportEnabled(boolean)} is turned off.
13+
*/
14+
final class SimpleEditable implements Editable {
15+
private String text;
16+
17+
public SimpleEditable(CharSequence text) {
18+
this(text.toString());
19+
}
20+
21+
public SimpleEditable(String text) {
22+
this.text = text;
23+
}
24+
25+
@Override public Editable replace(int st, int en, CharSequence source, int start, int end) {
26+
text = text.replaceFirst(quote(text.substring(st, en)), quoteReplacement(source.toString()));
27+
return this;
28+
}
29+
30+
@Override public Editable replace(int st, int en, CharSequence text) {
31+
return replace(st, en, text, 0, text.length());
32+
}
33+
34+
@Override public Editable insert(int where, CharSequence text, int start, int end) {
35+
throw new UnsupportedOperationException("Not implemented.");
36+
}
37+
38+
@Override public Editable insert(int where, CharSequence text) {
39+
throw new UnsupportedOperationException("Not implemented.");
40+
}
41+
42+
@Override public Editable delete(int st, int en) {
43+
throw new UnsupportedOperationException("Not implemented.");
44+
}
45+
46+
@Override public Editable append(CharSequence text) {
47+
throw new UnsupportedOperationException("Not implemented.");
48+
}
49+
50+
@Override public Editable append(CharSequence text, int start, int end) {
51+
throw new UnsupportedOperationException("Not implemented.");
52+
}
53+
54+
@Override public Editable append(char text) {
55+
throw new UnsupportedOperationException("Not implemented.");
56+
}
57+
58+
@Override public void clear() {
59+
}
60+
61+
@Override public void clearSpans() {
62+
}
63+
64+
@Override public void setFilters(InputFilter[] filters) {
65+
}
66+
67+
@Override public InputFilter[] getFilters() {
68+
return new InputFilter[0];
69+
}
70+
71+
@Override public void getChars(int start, int end, char[] dest, int destoff) {
72+
}
73+
74+
@Override public void setSpan(Object what, int start, int end, int flags) {
75+
}
76+
77+
@Override public void removeSpan(Object what) {
78+
}
79+
80+
@Override public <T> T[] getSpans(int start, int end, Class<T> type) {
81+
return null;
82+
}
83+
84+
@Override public int getSpanStart(Object tag) {
85+
return 0;
86+
}
87+
88+
@Override public int getSpanEnd(Object tag) {
89+
return 0;
90+
}
91+
92+
@Override public int getSpanFlags(Object tag) {
93+
return 0;
94+
}
95+
96+
@Override public int nextSpanTransition(int start, int limit, Class type) {
97+
return 0;
98+
}
99+
100+
@Override public int length() {
101+
return text.length();
102+
}
103+
104+
@Override public char charAt(int index) {
105+
return text.charAt(index);
106+
}
107+
108+
@Override public CharSequence subSequence(int start, int end) {
109+
return text.subSequence(start, end);
110+
}
111+
112+
@NonNull @Override public String toString() {
113+
return text;
114+
}
115+
116+
@Override public boolean equals(Object o) {
117+
if (this == o) return true;
118+
if (o == null) return false;
119+
120+
if (o.getClass() == String.class) {
121+
return text.equals(o);
122+
}
123+
124+
if (getClass() != o.getClass()) return false;
125+
126+
SimpleEditable that = (SimpleEditable) o;
127+
128+
return text.equals(that.text);
129+
}
130+
131+
@Override public int hashCode() {
132+
return text.hashCode();
133+
}
134+
}

0 commit comments

Comments
 (0)