1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.base;
33
34
35 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36 import java.io.IOException;
37 import java.io.UncheckedIOException;
38 import java.nio.ByteBuffer;
39 import java.nio.CharBuffer;
40 import java.nio.charset.CharacterCodingException;
41 import java.nio.charset.CharsetDecoder;
42 import java.nio.charset.CharsetEncoder;
43 import java.nio.charset.CoderResult;
44 import java.nio.charset.CodingErrorAction;
45 import javax.annotation.Nonnull;
46
47 import sun.nio.cs.ArrayDecoder;
48 import sun.nio.cs.ArrayEncoder;
49
50 import java.nio.charset.StandardCharsets;
51
52
53
54
55
56 @SuppressFBWarnings("IICU_INCORRECT_INTERNAL_CLASS_USE")
57 public final class Strings {
58
59
60 public static final String EOL = System.getProperty("line.separator", "\n");
61
62
63 private static final boolean LENIENT_CODING = Boolean.getBoolean("spf4j.encoding.lenient");
64
65 private static final ThreadLocal<CharsetDecoder> UTF8_DECODER = new ThreadLocal<CharsetDecoder>() {
66
67 @Override
68 protected CharsetDecoder initialValue() {
69 return createUtf8Decoder();
70 }
71
72 };
73
74 private static final ThreadLocal<CharsetEncoder> UTF8_ENCODER = new ThreadLocal<CharsetEncoder>() {
75
76 @Override
77 protected CharsetEncoder initialValue() {
78 return createUtf8Encoder();
79 }
80
81 };
82
83 private Strings() {
84 }
85
86
87
88
89 @Deprecated
90 public static int distance(@Nonnull final String s1, @Nonnull final String s2) {
91 return CharSequences.distance(s1, s2);
92 }
93
94
95
96
97
98
99 @Deprecated
100 public static boolean contains(final String string, final char... chars) {
101 return containsAnyChars(string, chars);
102 }
103
104 public static boolean containsAnyChars(final String string, final char... chars) {
105 for (char c : chars) {
106 if (string.indexOf(c) >= 0) {
107 return true;
108 }
109 }
110 return false;
111 }
112
113
114
115
116 @Deprecated
117 public static boolean contains(final CharSequence string, final char... chars) {
118 return CharSequences.containsAnyChar(string, chars);
119 }
120
121 public static String withFirstCharLower(final String str) {
122 if (str.isEmpty()) {
123 return str;
124 }
125 char fc = str.charAt(0);
126 if (Character.isLowerCase(fc)) {
127 return str;
128 }
129 int l = str.length();
130 StringBuilder result = new StringBuilder(l);
131 result.append(Character.toLowerCase(fc));
132 for (int i = 1; i < l; i++) {
133 result.append(str.charAt(i));
134 }
135 return result.toString();
136 }
137
138
139
140
141
142
143
144 public static String methodToAttribute(final String prefix, final String str) {
145 int length = str.length();
146 int pl = prefix.length();
147 StringBuilder result = new StringBuilder(length - pl);
148 char fc = str.charAt(pl);
149 if (Character.isLowerCase(fc)) {
150 result.append(str, pl, length);
151 } else {
152 result.append(Character.toLowerCase(fc));
153 result.append(str, pl + 1, length);
154 }
155 return result.toString();
156 }
157
158
159
160
161
162
163
164 public static String attributeToMethod(final String prefix, final String str) {
165 int length = str.length();
166 StringBuilder result = new StringBuilder(length + prefix.length());
167 result.append(prefix);
168 char fc = str.charAt(0);
169 if (Character.isLowerCase(fc)) {
170 result.append(Character.toUpperCase(fc));
171 result.append(str, 1, length);
172 } else {
173 result.append(str);
174 }
175 return result.toString();
176 }
177
178
179
180 public static void writeReplaceWhitespaces(final String str, final char replacement, final Appendable writer)
181 throws IOException {
182 for (char c : steal(str)) {
183 if (Character.isWhitespace(c)) {
184 writer.append(replacement);
185 } else {
186 writer.append(c);
187 }
188 }
189 }
190
191
192
193
194
195
196
197
198 public static char[] steal(final String str) {
199 return UnsafeString.steal(str);
200 }
201
202
203
204
205
206
207
208 public static String wrap(final char[] chars) {
209 return UnsafeString.wrap(chars);
210 }
211
212 public static CharsetEncoder createUtf8Encoder() {
213 if (LENIENT_CODING) {
214 return StandardCharsets.UTF_8.newEncoder().onMalformedInput(CodingErrorAction.REPLACE)
215 .onUnmappableCharacter(CodingErrorAction.REPLACE);
216 } else {
217 return StandardCharsets.UTF_8.newEncoder().onMalformedInput(CodingErrorAction.REPORT)
218 .onUnmappableCharacter(CodingErrorAction.REPORT);
219 }
220 }
221
222
223 public static CharsetDecoder createUtf8Decoder() {
224 if (LENIENT_CODING) {
225 return StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPLACE)
226 .onUnmappableCharacter(CodingErrorAction.REPLACE);
227 } else {
228 return StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT)
229 .onUnmappableCharacter(CodingErrorAction.REPORT);
230 }
231 }
232
233 public static CharsetEncoder getUTF8CharsetEncoder() {
234 return UTF8_ENCODER.get();
235 }
236
237 public static CharsetDecoder getUTF8CharsetDecoder() {
238 return UTF8_DECODER.get();
239 }
240
241 @SuppressFBWarnings("SUA_SUSPICIOUS_UNINITIALIZED_ARRAY")
242 public static byte[] encode(final CharsetEncoder ce, final char[] ca, final int off, final int len) {
243 if (len == 0) {
244 return Arrays.EMPTY_BYTE_ARRAY;
245 }
246 byte[] ba = TLScratch.getBytesTmp(getmaxNrBytes(ce, len));
247 int nrBytes = encode(ce, ca, off, len, ba);
248 return java.util.Arrays.copyOf(ba, nrBytes);
249 }
250
251 public static int getmaxNrBytes(final CharsetEncoder ce, final int nrChars) {
252 return (int) (nrChars * (double) ce.maxBytesPerChar());
253 }
254
255 public static int encode(final CharsetEncoder ce, final char[] ca, final int off, final int len,
256 final byte[] targetArray) {
257 if (len == 0) {
258 return 0;
259 }
260 if (ce instanceof ArrayEncoder) {
261 return ((ArrayEncoder) ce).encode(ca, off, len, targetArray);
262 } else {
263 ce.reset();
264 ByteBuffer bb = ByteBuffer.wrap(targetArray);
265 CharBuffer cb = CharBuffer.wrap(ca, off, len);
266 try {
267 CoderResult cr = ce.encode(cb, bb, true);
268 if (!cr.isUnderflow()) {
269 cr.throwException();
270 }
271 cr = ce.flush(bb);
272 if (!cr.isUnderflow()) {
273 cr.throwException();
274 }
275 } catch (CharacterCodingException x) {
276 throw new InternalError("Should never throw a CharacterCodingException, probably a JVM issue", x);
277 }
278 return bb.position();
279 }
280 }
281
282 @SuppressFBWarnings("SUA_SUSPICIOUS_UNINITIALIZED_ARRAY")
283 public static String decode(final CharsetDecoder cd, final byte[] ba, final int off, final int len) {
284 if (len == 0) {
285 return "";
286 }
287 int en = (int) (len * (double) cd.maxCharsPerByte());
288 char[] ca = TLScratch.getCharsTmp(en);
289 if (cd instanceof ArrayDecoder) {
290 int clen = ((ArrayDecoder) cd).decode(ba, off, len, ca);
291 return new String(ca, 0, clen);
292 }
293 cd.reset();
294 ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
295 CharBuffer cb = CharBuffer.wrap(ca);
296 try {
297 CoderResult cr = cd.decode(bb, cb, true);
298 if (!cr.isUnderflow()) {
299 cr.throwException();
300 }
301 cr = cd.flush(cb);
302 if (!cr.isUnderflow()) {
303 cr.throwException();
304 }
305 } catch (CharacterCodingException x) {
306 throw new UncheckedIOException(x);
307 }
308 return new String(ca, 0, cb.position());
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323 public static String fromUtf8(final byte[] bytes) {
324 return decode(UTF8_DECODER.get(), bytes, 0, bytes.length);
325 }
326
327 public static String fromUtf8(final byte[] bytes, final int startIdx, final int length) {
328 return decode(UTF8_DECODER.get(), bytes, startIdx, length);
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342 public static byte[] toUtf8(final String str) {
343 final char[] chars = steal(str);
344 return encode(UTF8_ENCODER.get(), chars, 0, chars.length);
345 }
346
347
348
349
350 @Deprecated
351 public static int compareTo(@Nonnull final CharSequence s, @Nonnull final CharSequence t) {
352 return CharSequences.compareTo(s, t);
353 }
354
355 @Deprecated
356 public static boolean equals(@Nonnull final CharSequence s, @Nonnull final CharSequence t) {
357 return CharSequences.equals(s, t);
358 }
359
360 @Deprecated
361 public static int hashcode(final CharSequence cs) {
362 return CharSequences.hashcode(cs);
363 }
364
365 @Deprecated
366 public static CharSequence subSequence(final CharSequence seq, final int startIdx, final int endIdx) {
367 return CharSequences.subSequence(seq, startIdx, endIdx);
368 }
369
370 @Deprecated
371 public static boolean endsWith(final CharSequence qc, final CharSequence with) {
372 return CharSequences.endsWith(qc, with);
373 }
374
375
376
377
378
379
380
381 @Deprecated
382 public static void escapeJsonString(@Nonnull final String toEscape, final StringBuilder jsonString) {
383 AppendableUtils.escapeJsonString(toEscape, jsonString);
384 }
385
386 @Deprecated
387 public static void escapeJsonString(@Nonnull final String toEscape, final Appendable jsonString)
388 throws IOException {
389 AppendableUtils.escapeJsonString(toEscape, jsonString);
390 }
391
392 @Deprecated
393 public static void appendJsonStringEscapedChar(final char c, final StringBuilder jsonString) {
394 AppendableUtils.appendJsonStringEscapedChar(c, jsonString);
395 }
396
397 @Deprecated
398 public static void appendJsonStringEscapedChar(final char c, final Appendable jsonString) throws IOException {
399 AppendableUtils.appendJsonStringEscapedChar(c, jsonString);
400 }
401
402 @Deprecated
403 public static void appendUnsignedString(final StringBuilder sb, final long nr, final int shift) {
404 AppendableUtils.appendUnsignedString(sb, nr, shift);
405 }
406
407 @Deprecated
408 public static void appendUnsignedString(final StringBuilder sb, final int nr, final int shift) {
409 AppendableUtils.appendUnsignedString(sb, nr, shift);
410 }
411
412 @Deprecated
413 public static void appendUnsignedStringPadded(final StringBuilder sb, final int nr, final int shift,
414 final int padTo) {
415 AppendableUtils.appendUnsignedStringPadded(sb, nr, shift, padTo);
416 }
417
418 @Deprecated
419 public static void appendUnsignedStringPadded(final Appendable sb, final int nr, final int shift,
420 final int padTo) throws IOException {
421 AppendableUtils.appendUnsignedStringPadded(sb, nr, shift, padTo);
422 }
423
424 @Deprecated
425 public static void appendSpaces(final Appendable to, final int nrSpaces) throws IOException {
426 AppendableUtils.appendSpaces(to, nrSpaces);
427 }
428
429 @Deprecated
430 public static void appendSpaces(final StringBuilder to, final int nrSpaces) {
431 AppendableUtils.appendSpaces(to, nrSpaces);
432 }
433
434
435
436
437 @Deprecated
438 public static boolean regionMatches(final CharSequence t, final int toffset,
439 final CharSequence other, final int ooffset, final int plen) {
440 return CharSequences.regionMatches(t, toffset, other, ooffset, plen);
441 }
442
443 public static String truncate(@Nonnull final String value, final int length) {
444 if (value.length() > length) {
445 return value.substring(0, length);
446 } else {
447 return value;
448 }
449 }
450
451 @Nonnull
452 public static String commonPrefix(@Nonnull final CharSequence... strs) {
453 if (strs.length <= 0) {
454 throw new IllegalArgumentException("Must have at least 1 string " + java.util.Arrays.toString(strs));
455 }
456 CharSequence common = strs[0];
457 for (int i = 1; i < strs.length; i++) {
458 common = com.google.common.base.Strings.commonPrefix(common, strs[i]);
459 }
460 return common.toString();
461 }
462
463 }