]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/util/StringUtil.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / StringUtil.java
1 //
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.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.util;
20
21 import java.io.UnsupportedEncodingException;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
24
25 import org.eclipse.jetty.util.log.Log;
26 import org.eclipse.jetty.util.log.Logger;
27
28 /** Fast String Utilities.
29  *
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.
34  *
35  * 
36  */
37 public class StringUtil
38 {
39     private static final Logger LOG = Log.getLogger(StringUtil.class);
40     
41     
42     private final static Trie<String> CHARSETS= new ArrayTrie<>(256);
43     
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");
48        
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";
52
53     /**
54      * @deprecated Use {@link StandardCharsets#UTF_8}
55      */
56     @Deprecated
57     public final static Charset __UTF8_CHARSET=StandardCharsets.UTF_8;
58     /**
59      * @deprecated Use {@link StandardCharsets#ISO_8859_1}
60      */
61     @Deprecated
62     public final static Charset __ISO_8859_1_CHARSET=StandardCharsets.ISO_8859_1;
63     /**
64      * @deprecated Use {@link StandardCharsets#UTF_16}
65      */
66     @Deprecated
67     public final static Charset __UTF16_CHARSET=StandardCharsets.UTF_16;
68     /**
69      * @deprecated Use {@link StandardCharsets#US_ASCII}
70      */
71     @Deprecated
72     public final static Charset __US_ASCII_CHARSET=StandardCharsets.US_ASCII;
73     
74     static
75     {
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);
82     }
83     
84     /* ------------------------------------------------------------ */
85     /** Convert alternate charset names (eg utf8) to normalized
86      * name (eg UTF-8).
87      */
88     public static String normalizeCharset(String s)
89     {
90         String n=CHARSETS.get(s);
91         return (n==null)?s:n;
92     }
93     
94     /* ------------------------------------------------------------ */
95     /** Convert alternate charset names (eg utf8) to normalized
96      * name (eg UTF-8).
97      */
98     public static String normalizeCharset(String s,int offset,int length)
99     {
100         String n=CHARSETS.get(s,offset,length);       
101         return (n==null)?s.substring(offset,offset+length):n;
102     }
103     
104
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' };
123
124     /* ------------------------------------------------------------ */
125     /**
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
129      */
130     public static String asciiToLowerCase(String s)
131     {
132         char[] c = null;
133         int i=s.length();
134
135         // look for first conversion
136         while (i-->0)
137         {
138             char c1=s.charAt(i);
139             if (c1<=127)
140             {
141                 char c2=lowercases[c1];
142                 if (c1!=c2)
143                 {
144                     c=s.toCharArray();
145                     c[i]=c2;
146                     break;
147                 }
148             }
149         }
150
151         while (i-->0)
152         {
153             if(c[i]<=127)
154                 c[i] = lowercases[c[i]];
155         }
156         
157         return c==null?s:new String(c);
158     }
159
160
161     /* ------------------------------------------------------------ */
162     public static boolean startsWithIgnoreCase(String s,String w)
163     {
164         if (w==null)
165             return true;
166         
167         if (s==null || s.length()<w.length())
168             return false;
169         
170         for (int i=0;i<w.length();i++)
171         {
172             char c1=s.charAt(i);
173             char c2=w.charAt(i);
174             if (c1!=c2)
175             {
176                 if (c1<=127)
177                     c1=lowercases[c1];
178                 if (c2<=127)
179                     c2=lowercases[c2];
180                 if (c1!=c2)
181                     return false;
182             }
183         }
184         return true;
185     }
186     
187     /* ------------------------------------------------------------ */
188     public static boolean endsWithIgnoreCase(String s,String w)
189     {
190         if (w==null)
191             return true;
192
193         if (s==null)
194             return false;
195             
196         int sl=s.length();
197         int wl=w.length();
198         
199         if (sl<wl)
200             return false;
201         
202         for (int i=wl;i-->0;)
203         {
204             char c1=s.charAt(--sl);
205             char c2=w.charAt(i);
206             if (c1!=c2)
207             {
208                 if (c1<=127)
209                     c1=lowercases[c1];
210                 if (c2<=127)
211                     c2=lowercases[c2];
212                 if (c1!=c2)
213                     return false;
214             }
215         }
216         return true;
217     }
218     
219     /* ------------------------------------------------------------ */
220     /**
221      * returns the next index of a character from the chars string
222      */
223     public static int indexFrom(String s,String chars)
224     {
225         for (int i=0;i<s.length();i++)
226            if (chars.indexOf(s.charAt(i))>=0)
227               return i;
228         return -1;
229     }
230     
231     /* ------------------------------------------------------------ */
232     /**
233      * replace substrings within string.
234      */
235     public static String replace(String s, String sub, String with)
236     {
237         int c=0;
238         int i=s.indexOf(sub,c);
239         if (i == -1)
240             return s;
241     
242         StringBuilder buf = new StringBuilder(s.length()+with.length());
243
244         do
245         {
246             buf.append(s.substring(c,i));
247             buf.append(with);
248             c=i+sub.length();
249         } while ((i=s.indexOf(sub,c))!=-1);
250
251         if (c<s.length())
252             buf.append(s.substring(c,s.length()));
253
254         return buf.toString();
255         
256     }
257
258
259     /* ------------------------------------------------------------ */
260     /** Remove single or double quotes.
261      */
262     public static String unquote(String s)
263     {
264         return QuotedStringTokenizer.unquote(s);
265     }
266
267
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
274      */
275     public static void append(StringBuilder buf,
276                               String s,
277                               int offset,
278                               int length)
279     {
280         synchronized(buf)
281         {
282             int end=offset+length;
283             for (int i=offset; i<end;i++)
284             {
285                 if (i>=s.length())
286                     break;
287                 buf.append(s.charAt(i));
288             }
289         }
290     }
291
292     
293     /* ------------------------------------------------------------ */
294     /**
295      * append hex digit
296      * 
297      */
298     public static void append(StringBuilder buf,byte b,int base)
299     {
300         int bi=0xff&b;
301         int c='0'+(bi/base)%base;
302         if (c>'9')
303             c= 'a'+(c-'0'-10);
304         buf.append((char)c);
305         c='0'+bi%base;
306         if (c>'9')
307             c= 'a'+(c-'0'-10);
308         buf.append((char)c);
309     }
310
311     /* ------------------------------------------------------------ */
312     public static void append2digits(StringBuffer buf,int i)
313     {
314         if (i<100)
315         {
316             buf.append((char)(i/10+'0'));
317             buf.append((char)(i%10+'0'));
318         }
319     }
320     
321     /* ------------------------------------------------------------ */
322     public static void append2digits(StringBuilder buf,int i)
323     {
324         if (i<100)
325         {
326             buf.append((char)(i/10+'0'));
327             buf.append((char)(i%10+'0'));
328         }
329     }
330     
331     /* ------------------------------------------------------------ */
332     /** Return a non null string.
333      * @param s String
334      * @return The string passed in or empty string if it is null. 
335      */
336     public static String nonNull(String s)
337     {
338         if (s==null)
339             return "";
340         return s;
341     }
342     
343     /* ------------------------------------------------------------ */
344     public static boolean equals(String s,char[] buf, int offset, int length)
345     {
346         if (s.length()!=length)
347             return false;
348         for (int i=0;i<length;i++)
349             if (buf[offset+i]!=s.charAt(i))
350                 return false;
351         return true;
352     }
353
354     /* ------------------------------------------------------------ */
355     public static String toUTF8String(byte[] b,int offset,int length)
356     {
357         return new String(b,offset,length,StandardCharsets.UTF_8);
358     }
359
360     /* ------------------------------------------------------------ */
361     public static String toString(byte[] b,int offset,int length,String charset)
362     {
363         try
364         {
365             return new String(b,offset,length,charset);
366         }
367         catch (UnsupportedEncodingException e)
368         {
369             throw new IllegalArgumentException(e);
370         }
371     }
372
373     /* ------------------------------------------------------------ */
374     /**
375      * Test if a string is null or only has whitespace characters in it.
376      * <p>
377      * Note: uses codepoint version of {@link Character#isWhitespace(int)} to support Unicode better.
378      * 
379      * <pre>
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
388      * </pre>
389      * 
390      * @param str
391      *            the string to test.
392      * @return true if string is null or only whitespace characters, false if non-whitespace characters encountered.
393      */
394     public static boolean isBlank(String str)
395     {
396         if (str == null)
397         {
398             return true;
399         }
400         int len = str.length();
401         for (int i = 0; i < len; i++)
402         {
403             if (!Character.isWhitespace(str.codePointAt(i)))
404             {
405                 // found a non-whitespace, we can stop searching  now
406                 return false;
407             }
408         }
409         // only whitespace
410         return true;
411     }
412     
413     /* ------------------------------------------------------------ */
414     /**
415      * Test if a string is not null and contains at least 1 non-whitespace characters in it.
416      * <p>
417      * Note: uses codepoint version of {@link Character#isWhitespace(int)} to support Unicode better.
418      * 
419      * <pre>
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
428      * </pre>
429      * 
430      * @param str
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.
433      */
434     public static boolean isNotBlank(String str)
435     {
436         if (str == null)
437         {
438             return false;
439         }
440         int len = str.length();
441         for (int i = 0; i < len; i++)
442         {
443             if (!Character.isWhitespace(str.codePointAt(i)))
444             {
445                 // found a non-whitespace, we can stop searching  now
446                 return true;
447             }
448         }
449         // only whitespace
450         return false;
451     }
452
453     /* ------------------------------------------------------------ */
454     public static boolean isUTF8(String charset)
455     {
456         return __UTF8.equalsIgnoreCase(charset)||__UTF8.equalsIgnoreCase(normalizeCharset(charset));
457     }
458
459
460     /* ------------------------------------------------------------ */
461     public static String printable(String name)
462     {
463         if (name==null)
464             return null;
465         StringBuilder buf = new StringBuilder(name.length());
466         for (int i=0;i<name.length();i++)
467         {
468             char c=name.charAt(i);
469             if (!Character.isISOControl(c))
470                 buf.append(c);
471         }
472         return buf.toString();
473     }
474     
475     /* ------------------------------------------------------------ */
476     public static String printable(byte[] b)
477     {
478         StringBuilder buf = new StringBuilder();
479         for (int i=0;i<b.length;i++)
480         {
481             char c=(char)b[i];
482             if (Character.isWhitespace(c)|| c>' ' && c<0x7f)
483                 buf.append(c);
484             else 
485             {
486                 buf.append("0x");
487                 TypeUtil.toHex(b[i],buf);
488             }
489         }
490         return buf.toString();
491     }
492     
493     public static byte[] getBytes(String s)
494     {
495         return s.getBytes(StandardCharsets.ISO_8859_1);
496     }
497     
498     public static byte[] getUtf8Bytes(String s)
499     {
500         return s.getBytes(StandardCharsets.UTF_8);
501     }
502     
503     public static byte[] getBytes(String s,String charset)
504     {
505         try
506         {
507             return s.getBytes(charset);
508         }
509         catch(Exception e)
510         {
511             LOG.warn(e);
512             return s.getBytes();
513         }
514     }
515     
516     
517     
518     /**
519      * Converts a binary SID to a string SID
520      * 
521      * http://en.wikipedia.org/wiki/Security_Identifier
522      * 
523      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
524      */
525     public static String sidBytesToString(byte[] sidBytes)
526     {
527         StringBuilder sidString = new StringBuilder();
528         
529         // Identify this as a SID
530         sidString.append("S-");
531         
532         // Add SID revision level (expect 1 but may change someday)
533         sidString.append(Byte.toString(sidBytes[0])).append('-');
534         
535         StringBuilder tmpBuilder = new StringBuilder();
536         
537         // crunch the six bytes of issuing authority value
538         for (int i = 2; i <= 7; ++i)
539         {
540             tmpBuilder.append(Integer.toHexString(sidBytes[i] & 0xFF));
541         }
542         
543         sidString.append(Long.parseLong(tmpBuilder.toString(), 16)); // '-' is in the subauth loop
544    
545         // the number of subAuthorities we need to attach
546         int subAuthorityCount = sidBytes[1];
547
548         // attach each of the subAuthorities
549         for (int i = 0; i < subAuthorityCount; ++i)
550         {
551             int offset = i * 4;
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));
560         }
561         
562         return sidString.toString();
563     }
564     
565     /**
566      * Converts a string SID to a binary SID
567      * 
568      * http://en.wikipedia.org/wiki/Security_Identifier
569      * 
570      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
571      */
572     public static byte[] sidStringToBytes( String sidString )
573     {
574         String[] sidTokens = sidString.split("-");
575         
576         int subAuthorityCount = sidTokens.length - 3; // S-Rev-IdAuth-
577         
578         int byteCount = 0;
579         byte[] sidBytes = new byte[1 + 1 + 6 + (4 * subAuthorityCount)];
580         
581         // the revision byte
582         sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]);
583
584         // the # of sub authorities byte
585         sidBytes[byteCount++] = (byte)subAuthorityCount;
586
587         // the certAuthority
588         String hexStr = Long.toHexString(Long.parseLong(sidTokens[2]));
589         
590         while( hexStr.length() < 12) // pad to 12 characters
591         {
592             hexStr = "0" + hexStr;
593         }
594
595         // place the certAuthority 6 bytes
596         for ( int i = 0 ; i < hexStr.length(); i = i + 2)
597         {
598             sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(i, i + 2),16);
599         }
600                 
601         
602         for ( int i = 3; i < sidTokens.length ; ++i)
603         {
604             hexStr = Long.toHexString(Long.parseLong(sidTokens[i]));
605             
606             while( hexStr.length() < 8) // pad to 8 characters
607             {
608                 hexStr = "0" + hexStr;
609             }     
610             
611             // place the inverted sub authorities, 4 bytes each
612             for ( int j = hexStr.length(); j > 0; j = j - 2)
613             {          
614                 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(j-2, j),16);
615             }
616         }
617       
618         return sidBytes;
619     }
620     
621
622     /**
623      * Convert String to an integer. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
624      * 
625      * @param string
626      *            A String containing an integer.
627      * @return an int
628      */
629     public static int toInt(String string)
630     {
631         int val = 0;
632         boolean started = false;
633         boolean minus = false;
634
635         for (int i = 0; i < string.length(); i++)
636         {
637             char b = string.charAt(i);
638             if (b <= ' ')
639             {
640                 if (started)
641                     break;
642             }
643             else if (b >= '0' && b <= '9')
644             {
645                 val = val * 10 + (b - '0');
646                 started = true;
647             }
648             else if (b == '-' && !started)
649             {
650                 minus = true;
651             }
652             else
653                 break;
654         }
655
656         if (started)
657             return minus?(-val):val;
658         throw new NumberFormatException(string);
659     }
660
661     /**
662      * Convert String to an long. Parses up to the first non-numeric character. If no number is found an IllegalArgumentException is thrown
663      * 
664      * @param string
665      *            A String containing an integer.
666      * @return an int
667      */
668     public static long toLong(String string)
669     {
670         long val = 0;
671         boolean started = false;
672         boolean minus = false;
673
674         for (int i = 0; i < string.length(); i++)
675         {
676             char b = string.charAt(i);
677             if (b <= ' ')
678             {
679                 if (started)
680                     break;
681             }
682             else if (b >= '0' && b <= '9')
683             {
684                 val = val * 10L + (b - '0');
685                 started = true;
686             }
687             else if (b == '-' && !started)
688             {
689                 minus = true;
690             }
691             else
692                 break;
693         }
694
695         if (started)
696             return minus?(-val):val;
697         throw new NumberFormatException(string);
698     }
699     
700     /**
701      * Truncate a string to a max size.
702      * 
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.
706      */
707     public static String truncate(String str, int maxSize)
708     {
709         if (str == null)
710         {
711             return null;
712         }
713
714         if (str.length() <= maxSize)
715         {
716             return str;
717         }
718
719         return str.substring(0,maxSize);
720     }
721
722     public static String[] arrayFromString(String s) 
723     {
724         if (s==null)
725             return new String[]{};
726
727         if (!s.startsWith("[") || !s.endsWith("]"))
728             throw new IllegalArgumentException();
729         if (s.length()==2)
730             return new String[]{};
731
732         return s.substring(1,s.length()-1).split(" *, *");
733     }
734
735 }