2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util;
21 import java.io.UnsupportedEncodingException;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
25 import org.eclipse.jetty.util.log.Log;
26 import org.eclipse.jetty.util.log.Logger;
28 /** Fast String Utilities.
30 * These string utilities provide both convenience methods and
31 * performance improvements over most standard library versions. The
32 * main aim of the optimizations is to avoid object creation unless
33 * absolutely required.
37 public class StringUtil
39 private static final Logger LOG = Log.getLogger(StringUtil.class);
42 private final static Trie<String> CHARSETS= new ArrayTrie<>(256);
44 public static final String ALL_INTERFACES="0.0.0.0";
45 public static final String CRLF="\015\012";
46 public static final String __LINE_SEPARATOR=
47 System.getProperty("line.separator","\n");
49 public static final String __ISO_8859_1="ISO-8859-1";
50 public final static String __UTF8="UTF-8";
51 public final static String __UTF16="UTF-16";
54 * @deprecated Use {@link StandardCharsets#UTF_8}
57 public final static Charset __UTF8_CHARSET=StandardCharsets.UTF_8;
59 * @deprecated Use {@link StandardCharsets#ISO_8859_1}
62 public final static Charset __ISO_8859_1_CHARSET=StandardCharsets.ISO_8859_1;
64 * @deprecated Use {@link StandardCharsets#UTF_16}
67 public final static Charset __UTF16_CHARSET=StandardCharsets.UTF_16;
69 * @deprecated Use {@link StandardCharsets#US_ASCII}
72 public final static Charset __US_ASCII_CHARSET=StandardCharsets.US_ASCII;
76 CHARSETS.put("UTF-8",__UTF8);
77 CHARSETS.put("UTF8",__UTF8);
78 CHARSETS.put("UTF-16",__UTF16);
79 CHARSETS.put("UTF16",__UTF16);
80 CHARSETS.put("ISO-8859-1",__ISO_8859_1);
81 CHARSETS.put("ISO_8859_1",__ISO_8859_1);
84 /* ------------------------------------------------------------ */
85 /** Convert alternate charset names (eg utf8) to normalized
88 public static String normalizeCharset(String s)
90 String n=CHARSETS.get(s);
94 /* ------------------------------------------------------------ */
95 /** Convert alternate charset names (eg utf8) to normalized
98 public static String normalizeCharset(String s,int offset,int length)
100 String n=CHARSETS.get(s,offset,length);
101 return (n==null)?s.substring(offset,offset+length):n;
105 /* ------------------------------------------------------------ */
106 public static final char[] lowercases = {
107 '\000','\001','\002','\003','\004','\005','\006','\007',
108 '\010','\011','\012','\013','\014','\015','\016','\017',
109 '\020','\021','\022','\023','\024','\025','\026','\027',
110 '\030','\031','\032','\033','\034','\035','\036','\037',
111 '\040','\041','\042','\043','\044','\045','\046','\047',
112 '\050','\051','\052','\053','\054','\055','\056','\057',
113 '\060','\061','\062','\063','\064','\065','\066','\067',
114 '\070','\071','\072','\073','\074','\075','\076','\077',
115 '\100','\141','\142','\143','\144','\145','\146','\147',
116 '\150','\151','\152','\153','\154','\155','\156','\157',
117 '\160','\161','\162','\163','\164','\165','\166','\167',
118 '\170','\171','\172','\133','\134','\135','\136','\137',
119 '\140','\141','\142','\143','\144','\145','\146','\147',
120 '\150','\151','\152','\153','\154','\155','\156','\157',
121 '\160','\161','\162','\163','\164','\165','\166','\167',
122 '\170','\171','\172','\173','\174','\175','\176','\177' };
124 /* ------------------------------------------------------------ */
126 * fast lower case conversion. Only works on ascii (not unicode)
127 * @param s the string to convert
128 * @return a lower case version of s
130 public static String asciiToLowerCase(String s)
135 // look for first conversion
141 char c2=lowercases[c1];
154 c[i] = lowercases[c[i]];
157 return c==null?s:new String(c);
161 /* ------------------------------------------------------------ */
162 public static boolean startsWithIgnoreCase(String s,String w)
167 if (s==null || s.length()<w.length())
170 for (int i=0;i<w.length();i++)
187 /* ------------------------------------------------------------ */
188 public static boolean endsWithIgnoreCase(String s,String w)
202 for (int i=wl;i-->0;)
204 char c1=s.charAt(--sl);
219 /* ------------------------------------------------------------ */
221 * returns the next index of a character from the chars string
223 public static int indexFrom(String s,String chars)
225 for (int i=0;i<s.length();i++)
226 if (chars.indexOf(s.charAt(i))>=0)
231 /* ------------------------------------------------------------ */
233 * replace substrings within string.
235 public static String replace(String s, String sub, String with)
238 int i=s.indexOf(sub,c);
242 StringBuilder buf = new StringBuilder(s.length()+with.length());
246 buf.append(s.substring(c,i));
249 } while ((i=s.indexOf(sub,c))!=-1);
252 buf.append(s.substring(c,s.length()));
254 return buf.toString();
259 /* ------------------------------------------------------------ */
260 /** Remove single or double quotes.
262 public static String unquote(String s)
264 return QuotedStringTokenizer.unquote(s);
268 /* ------------------------------------------------------------ */
269 /** Append substring to StringBuilder
270 * @param buf StringBuilder to append to
271 * @param s String to append from
272 * @param offset The offset of the substring
273 * @param length The length of the substring
275 public static void append(StringBuilder buf,
282 int end=offset+length;
283 for (int i=offset; i<end;i++)
287 buf.append(s.charAt(i));
293 /* ------------------------------------------------------------ */
298 public static void append(StringBuilder buf,byte b,int base)
301 int c='0'+(bi/base)%base;
311 /* ------------------------------------------------------------ */
312 public static void append2digits(StringBuffer buf,int i)
316 buf.append((char)(i/10+'0'));
317 buf.append((char)(i%10+'0'));
321 /* ------------------------------------------------------------ */
322 public static void append2digits(StringBuilder buf,int i)
326 buf.append((char)(i/10+'0'));
327 buf.append((char)(i%10+'0'));
331 /* ------------------------------------------------------------ */
332 /** Return a non null string.
334 * @return The string passed in or empty string if it is null.
336 public static String nonNull(String s)
343 /* ------------------------------------------------------------ */
344 public static boolean equals(String s,char[] buf, int offset, int length)
346 if (s.length()!=length)
348 for (int i=0;i<length;i++)
349 if (buf[offset+i]!=s.charAt(i))
354 /* ------------------------------------------------------------ */
355 public static String toUTF8String(byte[] b,int offset,int length)
357 return new String(b,offset,length,StandardCharsets.UTF_8);
360 /* ------------------------------------------------------------ */
361 public static String toString(byte[] b,int offset,int length,String charset)
365 return new String(b,offset,length,charset);
367 catch (UnsupportedEncodingException e)
369 throw new IllegalArgumentException(e);
373 /* ------------------------------------------------------------ */
375 * Test if a string is null or only has whitespace characters in it.
377 * Note: uses codepoint version of {@link Character#isWhitespace(int)} to support Unicode better.
380 * isBlank(null) == true
381 * isBlank("") == true
382 * isBlank("\r\n") == true
383 * isBlank("\t") == true
384 * isBlank(" ") == true
385 * isBlank("a") == false
386 * isBlank(".") == false
387 * isBlank(";\n") == false
391 * the string to test.
392 * @return true if string is null or only whitespace characters, false if non-whitespace characters encountered.
394 public static boolean isBlank(String str)
400 int len = str.length();
401 for (int i = 0; i < len; i++)
403 if (!Character.isWhitespace(str.codePointAt(i)))
405 // found a non-whitespace, we can stop searching now
413 /* ------------------------------------------------------------ */
415 * Test if a string is not null and contains at least 1 non-whitespace characters in it.
417 * Note: uses codepoint version of {@link Character#isWhitespace(int)} to support Unicode better.
420 * isNotBlank(null) == false
421 * isNotBlank("") == false
422 * isNotBlank("\r\n") == false
423 * isNotBlank("\t") == false
424 * isNotBlank(" ") == false
425 * isNotBlank("a") == true
426 * isNotBlank(".") == true
427 * isNotBlank(";\n") == true
431 * the string to test.
432 * @return true if string is not null and has at least 1 non-whitespace character, false if null or all-whitespace characters.
434 public static boolean isNotBlank(String str)
440 int len = str.length();
441 for (int i = 0; i < len; i++)
443 if (!Character.isWhitespace(str.codePointAt(i)))
445 // found a non-whitespace, we can stop searching now
453 /* ------------------------------------------------------------ */
454 public static boolean isUTF8(String charset)
456 return __UTF8.equalsIgnoreCase(charset)||__UTF8.equalsIgnoreCase(normalizeCharset(charset));
460 /* ------------------------------------------------------------ */
461 public static String printable(String name)
465 StringBuilder buf = new StringBuilder(name.length());
466 for (int i=0;i<name.length();i++)
468 char c=name.charAt(i);
469 if (!Character.isISOControl(c))
472 return buf.toString();
475 /* ------------------------------------------------------------ */
476 public static String printable(byte[] b)
478 StringBuilder buf = new StringBuilder();
479 for (int i=0;i<b.length;i++)
482 if (Character.isWhitespace(c)|| c>' ' && c<0x7f)
487 TypeUtil.toHex(b[i],buf);
490 return buf.toString();
493 public static byte[] getBytes(String s)
495 return s.getBytes(StandardCharsets.ISO_8859_1);
498 public static byte[] getUtf8Bytes(String s)
500 return s.getBytes(StandardCharsets.UTF_8);
503 public static byte[] getBytes(String s,String charset)
507 return s.getBytes(charset);
519 * Converts a binary SID to a string SID
521 * http://en.wikipedia.org/wiki/Security_Identifier
523 * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
525 public static String sidBytesToString(byte[] sidBytes)
527 StringBuilder sidString = new StringBuilder();
529 // Identify this as a SID
530 sidString.append("S-");
532 // Add SID revision level (expect 1 but may change someday)
533 sidString.append(Byte.toString(sidBytes[0])).append('-');
535 StringBuilder tmpBuilder = new StringBuilder();
537 // crunch the six bytes of issuing authority value
538 for (int i = 2; i <= 7; ++i)
540 tmpBuilder.append(Integer.toHexString(sidBytes[i] & 0xFF));
543 sidString.append(Long.parseLong(tmpBuilder.toString(), 16)); // '-' is in the subauth loop
545 // the number of subAuthorities we need to attach
546 int subAuthorityCount = sidBytes[1];
548 // attach each of the subAuthorities
549 for (int i = 0; i < subAuthorityCount; ++i)
552 tmpBuilder.setLength(0);
553 // these need to be zero padded hex and little endian
554 tmpBuilder.append(String.format("%02X%02X%02X%02X",
555 (sidBytes[11 + offset] & 0xFF),
556 (sidBytes[10 + offset] & 0xFF),
557 (sidBytes[9 + offset] & 0xFF),
558 (sidBytes[8 + offset] & 0xFF)));
559 sidString.append('-').append(Long.parseLong(tmpBuilder.toString(), 16));
562 return sidString.toString();
566 * Converts a string SID to a binary SID
568 * http://en.wikipedia.org/wiki/Security_Identifier
570 * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
572 public static byte[] sidStringToBytes( String sidString )
574 String[] sidTokens = sidString.split("-");
576 int subAuthorityCount = sidTokens.length - 3; // S-Rev-IdAuth-
579 byte[] sidBytes = new byte[1 + 1 + 6 + (4 * subAuthorityCount)];
582 sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]);
584 // the # of sub authorities byte
585 sidBytes[byteCount++] = (byte)subAuthorityCount;
588 String hexStr = Long.toHexString(Long.parseLong(sidTokens[2]));
590 while( hexStr.length() < 12) // pad to 12 characters
592 hexStr = "0" + hexStr;
595 // place the certAuthority 6 bytes
596 for ( int i = 0 ; i < hexStr.length(); i = i + 2)
598 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(i, i + 2),16);
602 for ( int i = 3; i < sidTokens.length ; ++i)
604 hexStr = Long.toHexString(Long.parseLong(sidTokens[i]));
606 while( hexStr.length() < 8) // pad to 8 characters
608 hexStr = "0" + hexStr;
611 // place the inverted sub authorities, 4 bytes each
612 for ( int j = hexStr.length(); j > 0; j = j - 2)
614 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(j-2, j),16);
623 * Convert String to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
626 * A String containing an integer.
629 public static int toInt(String string)
632 boolean started = false;
633 boolean minus = false;
635 for (int i = 0; i < string.length(); i++)
637 char b = string.charAt(i);
643 else if (b >= '0' && b <= '9')
645 val = val * 10 + (b - '0');
648 else if (b == '-' && !started)
657 return minus?(-val):val;
658 throw new NumberFormatException(string);
662 * Convert String to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
665 * A String containing an integer.
668 public static long toLong(String string)
671 boolean started = false;
672 boolean minus = false;
674 for (int i = 0; i < string.length(); i++)
676 char b = string.charAt(i);
682 else if (b >= '0' && b <= '9')
684 val = val * 10L + (b - '0');
687 else if (b == '-' && !started)
696 return minus?(-val):val;
697 throw new NumberFormatException(string);
701 * Truncate a string to a max size.
703 * @param str the string to possibly truncate
704 * @param maxSize the maximum size of the string
705 * @return the truncated string. if <code>str</code> param is null, then the returned string will also be null.
707 public static String truncate(String str, int maxSize)
714 if (str.length() <= maxSize)
719 return str.substring(0,maxSize);
722 public static String[] arrayFromString(String s)
725 return new String[]{};
727 if (!s.startsWith("[") || !s.endsWith("]"))
728 throw new IllegalArgumentException();
730 return new String[]{};
732 return s.substring(1,s.length()-1).split(" *, *");