• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 import org.eclipse.jetty.util.log.Log;
25 import org.eclipse.jetty.util.log.Logger;
26 
27 /** Fast String Utilities.
28  *
29  * These string utilities provide both conveniance methods and
30  * performance improvements over most standard library versions. The
31  * main aim of the optimizations is to avoid object creation unless
32  * absolutely required.
33  *
34  *
35  */
36 public class StringUtil
37 {
38     private static final Logger LOG = Log.getLogger(StringUtil.class);
39 
40     public static final String ALL_INTERFACES="0.0.0.0";
41     public static final String CRLF="\015\012";
42     public static final String __LINE_SEPARATOR=
43         System.getProperty("line.separator","\n");
44 
45     public static final String __ISO_8859_1="ISO-8859-1";
46     public final static String __UTF8="UTF-8";
47     public final static String __UTF8Alt="UTF8";
48     public final static String __UTF16="UTF-16";
49 
50     public final static Charset __UTF8_CHARSET;
51     public final static Charset __ISO_8859_1_CHARSET;
52 
53     static
54     {
55         __UTF8_CHARSET=Charset.forName(__UTF8);
56         __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1);
57     }
58 
59     private static char[] lowercases = {
60           '\000','\001','\002','\003','\004','\005','\006','\007',
61           '\010','\011','\012','\013','\014','\015','\016','\017',
62           '\020','\021','\022','\023','\024','\025','\026','\027',
63           '\030','\031','\032','\033','\034','\035','\036','\037',
64           '\040','\041','\042','\043','\044','\045','\046','\047',
65           '\050','\051','\052','\053','\054','\055','\056','\057',
66           '\060','\061','\062','\063','\064','\065','\066','\067',
67           '\070','\071','\072','\073','\074','\075','\076','\077',
68           '\100','\141','\142','\143','\144','\145','\146','\147',
69           '\150','\151','\152','\153','\154','\155','\156','\157',
70           '\160','\161','\162','\163','\164','\165','\166','\167',
71           '\170','\171','\172','\133','\134','\135','\136','\137',
72           '\140','\141','\142','\143','\144','\145','\146','\147',
73           '\150','\151','\152','\153','\154','\155','\156','\157',
74           '\160','\161','\162','\163','\164','\165','\166','\167',
75           '\170','\171','\172','\173','\174','\175','\176','\177' };
76 
77     /* ------------------------------------------------------------ */
78     /**
79      * fast lower case conversion. Only works on ascii (not unicode)
80      * @param s the string to convert
81      * @return a lower case version of s
82      */
asciiToLowerCase(String s)83     public static String asciiToLowerCase(String s)
84     {
85         char[] c = null;
86         int i=s.length();
87 
88         // look for first conversion
89         while (i-->0)
90         {
91             char c1=s.charAt(i);
92             if (c1<=127)
93             {
94                 char c2=lowercases[c1];
95                 if (c1!=c2)
96                 {
97                     c=s.toCharArray();
98                     c[i]=c2;
99                     break;
100                 }
101             }
102         }
103 
104         while (i-->0)
105         {
106             if(c[i]<=127)
107                 c[i] = lowercases[c[i]];
108         }
109 
110         return c==null?s:new String(c);
111     }
112 
113 
114     /* ------------------------------------------------------------ */
startsWithIgnoreCase(String s,String w)115     public static boolean startsWithIgnoreCase(String s,String w)
116     {
117         if (w==null)
118             return true;
119 
120         if (s==null || s.length()<w.length())
121             return false;
122 
123         for (int i=0;i<w.length();i++)
124         {
125             char c1=s.charAt(i);
126             char c2=w.charAt(i);
127             if (c1!=c2)
128             {
129                 if (c1<=127)
130                     c1=lowercases[c1];
131                 if (c2<=127)
132                     c2=lowercases[c2];
133                 if (c1!=c2)
134                     return false;
135             }
136         }
137         return true;
138     }
139 
140     /* ------------------------------------------------------------ */
endsWithIgnoreCase(String s,String w)141     public static boolean endsWithIgnoreCase(String s,String w)
142     {
143         if (w==null)
144             return true;
145 
146         if (s==null)
147             return false;
148 
149         int sl=s.length();
150         int wl=w.length();
151 
152         if (sl<wl)
153             return false;
154 
155         for (int i=wl;i-->0;)
156         {
157             char c1=s.charAt(--sl);
158             char c2=w.charAt(i);
159             if (c1!=c2)
160             {
161                 if (c1<=127)
162                     c1=lowercases[c1];
163                 if (c2<=127)
164                     c2=lowercases[c2];
165                 if (c1!=c2)
166                     return false;
167             }
168         }
169         return true;
170     }
171 
172     /* ------------------------------------------------------------ */
173     /**
174      * returns the next index of a character from the chars string
175      */
indexFrom(String s,String chars)176     public static int indexFrom(String s,String chars)
177     {
178         for (int i=0;i<s.length();i++)
179            if (chars.indexOf(s.charAt(i))>=0)
180               return i;
181         return -1;
182     }
183 
184     /* ------------------------------------------------------------ */
185     /**
186      * replace substrings within string.
187      */
replace(String s, String sub, String with)188     public static String replace(String s, String sub, String with)
189     {
190         int c=0;
191         int i=s.indexOf(sub,c);
192         if (i == -1)
193             return s;
194 
195         StringBuilder buf = new StringBuilder(s.length()+with.length());
196 
197         do
198         {
199             buf.append(s.substring(c,i));
200             buf.append(with);
201             c=i+sub.length();
202         } while ((i=s.indexOf(sub,c))!=-1);
203 
204         if (c<s.length())
205             buf.append(s.substring(c,s.length()));
206 
207         return buf.toString();
208 
209     }
210 
211 
212     /* ------------------------------------------------------------ */
213     /** Remove single or double quotes.
214      */
unquote(String s)215     public static String unquote(String s)
216     {
217         return QuotedStringTokenizer.unquote(s);
218     }
219 
220 
221     /* ------------------------------------------------------------ */
222     /** Append substring to StringBuilder
223      * @param buf StringBuilder to append to
224      * @param s String to append from
225      * @param offset The offset of the substring
226      * @param length The length of the substring
227      */
append(StringBuilder buf, String s, int offset, int length)228     public static void append(StringBuilder buf,
229                               String s,
230                               int offset,
231                               int length)
232     {
233         synchronized(buf)
234         {
235             int end=offset+length;
236             for (int i=offset; i<end;i++)
237             {
238                 if (i>=s.length())
239                     break;
240                 buf.append(s.charAt(i));
241             }
242         }
243     }
244 
245 
246     /* ------------------------------------------------------------ */
247     /**
248      * append hex digit
249      *
250      */
append(StringBuilder buf,byte b,int base)251     public static void append(StringBuilder buf,byte b,int base)
252     {
253         int bi=0xff&b;
254         int c='0'+(bi/base)%base;
255         if (c>'9')
256             c= 'a'+(c-'0'-10);
257         buf.append((char)c);
258         c='0'+bi%base;
259         if (c>'9')
260             c= 'a'+(c-'0'-10);
261         buf.append((char)c);
262     }
263 
264     /* ------------------------------------------------------------ */
append2digits(StringBuffer buf,int i)265     public static void append2digits(StringBuffer buf,int i)
266     {
267         if (i<100)
268         {
269             buf.append((char)(i/10+'0'));
270             buf.append((char)(i%10+'0'));
271         }
272     }
273 
274     /* ------------------------------------------------------------ */
append2digits(StringBuilder buf,int i)275     public static void append2digits(StringBuilder buf,int i)
276     {
277         if (i<100)
278         {
279             buf.append((char)(i/10+'0'));
280             buf.append((char)(i%10+'0'));
281         }
282     }
283 
284     /* ------------------------------------------------------------ */
285     /** Return a non null string.
286      * @param s String
287      * @return The string passed in or empty string if it is null.
288      */
nonNull(String s)289     public static String nonNull(String s)
290     {
291         if (s==null)
292             return "";
293         return s;
294     }
295 
296     /* ------------------------------------------------------------ */
equals(String s,char[] buf, int offset, int length)297     public static boolean equals(String s,char[] buf, int offset, int length)
298     {
299         if (s.length()!=length)
300             return false;
301         for (int i=0;i<length;i++)
302             if (buf[offset+i]!=s.charAt(i))
303                 return false;
304         return true;
305     }
306 
307     /* ------------------------------------------------------------ */
toUTF8String(byte[] b,int offset,int length)308     public static String toUTF8String(byte[] b,int offset,int length)
309     {
310         try
311         {
312             return new String(b,offset,length,__UTF8);
313         }
314         catch (UnsupportedEncodingException e)
315         {
316             throw new IllegalArgumentException(e);
317         }
318     }
319 
320     /* ------------------------------------------------------------ */
toString(byte[] b,int offset,int length,String charset)321     public static String toString(byte[] b,int offset,int length,String charset)
322     {
323         try
324         {
325             return new String(b,offset,length,charset);
326         }
327         catch (UnsupportedEncodingException e)
328         {
329             throw new IllegalArgumentException(e);
330         }
331     }
332 
333 
334     /* ------------------------------------------------------------ */
isUTF8(String charset)335     public static boolean isUTF8(String charset)
336     {
337         return __UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset);
338     }
339 
340 
341     /* ------------------------------------------------------------ */
printable(String name)342     public static String printable(String name)
343     {
344         if (name==null)
345             return null;
346         StringBuilder buf = new StringBuilder(name.length());
347         for (int i=0;i<name.length();i++)
348         {
349             char c=name.charAt(i);
350             if (!Character.isISOControl(c))
351                 buf.append(c);
352         }
353         return buf.toString();
354     }
355 
356     /* ------------------------------------------------------------ */
printable(byte[] b)357     public static String printable(byte[] b)
358     {
359         StringBuilder buf = new StringBuilder();
360         for (int i=0;i<b.length;i++)
361         {
362             char c=(char)b[i];
363             if (Character.isWhitespace(c)|| c>' ' && c<0x7f)
364                 buf.append(c);
365             else
366             {
367                 buf.append("0x");
368                 TypeUtil.toHex(b[i],buf);
369             }
370         }
371         return buf.toString();
372     }
373 
getBytes(String s)374     public static byte[] getBytes(String s)
375     {
376         try
377         {
378             return s.getBytes(__ISO_8859_1);
379         }
380         catch(Exception e)
381         {
382             LOG.warn(e);
383             return s.getBytes();
384         }
385     }
386 
getBytes(String s,String charset)387     public static byte[] getBytes(String s,String charset)
388     {
389         try
390         {
391             return s.getBytes(charset);
392         }
393         catch(Exception e)
394         {
395             LOG.warn(e);
396             return s.getBytes();
397         }
398     }
399 
400 
401 
402     /**
403      * Converts a binary SID to a string SID
404      *
405      * http://en.wikipedia.org/wiki/Security_Identifier
406      *
407      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
408      */
sidBytesToString(byte[] sidBytes)409     public static String sidBytesToString(byte[] sidBytes)
410     {
411         StringBuilder sidString = new StringBuilder();
412 
413         // Identify this as a SID
414         sidString.append("S-");
415 
416         // Add SID revision level (expect 1 but may change someday)
417         sidString.append(Byte.toString(sidBytes[0])).append('-');
418 
419         StringBuilder tmpBuilder = new StringBuilder();
420 
421         // crunch the six bytes of issuing authority value
422         for (int i = 2; i <= 7; ++i)
423         {
424             tmpBuilder.append(Integer.toHexString(sidBytes[i] & 0xFF));
425         }
426 
427         sidString.append(Long.parseLong(tmpBuilder.toString(), 16)); // '-' is in the subauth loop
428 
429         // the number of subAuthorities we need to attach
430         int subAuthorityCount = sidBytes[1];
431 
432         // attach each of the subAuthorities
433         for (int i = 0; i < subAuthorityCount; ++i)
434         {
435             int offset = i * 4;
436             tmpBuilder.setLength(0);
437             // these need to be zero padded hex and little endian
438             tmpBuilder.append(String.format("%02X%02X%02X%02X",
439                     (sidBytes[11 + offset] & 0xFF),
440                     (sidBytes[10 + offset] & 0xFF),
441                     (sidBytes[9 + offset] & 0xFF),
442                     (sidBytes[8 + offset] & 0xFF)));
443             sidString.append('-').append(Long.parseLong(tmpBuilder.toString(), 16));
444         }
445 
446         return sidString.toString();
447     }
448 
449     /**
450      * Converts a string SID to a binary SID
451      *
452      * http://en.wikipedia.org/wiki/Security_Identifier
453      *
454      * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
455      */
sidStringToBytes( String sidString )456     public static byte[] sidStringToBytes( String sidString )
457     {
458         String[] sidTokens = sidString.split("-");
459 
460         int subAuthorityCount = sidTokens.length - 3; // S-Rev-IdAuth-
461 
462         int byteCount = 0;
463         byte[] sidBytes = new byte[1 + 1 + 6 + (4 * subAuthorityCount)];
464 
465         // the revision byte
466         sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]);
467 
468         // the # of sub authorities byte
469         sidBytes[byteCount++] = (byte)subAuthorityCount;
470 
471         // the certAuthority
472         String hexStr = Long.toHexString(Long.parseLong(sidTokens[2]));
473 
474         while( hexStr.length() < 12) // pad to 12 characters
475         {
476             hexStr = "0" + hexStr;
477         }
478 
479         // place the certAuthority 6 bytes
480         for ( int i = 0 ; i < hexStr.length(); i = i + 2)
481         {
482             sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(i, i + 2),16);
483         }
484 
485 
486         for ( int i = 3; i < sidTokens.length ; ++i)
487         {
488             hexStr = Long.toHexString(Long.parseLong(sidTokens[i]));
489 
490             while( hexStr.length() < 8) // pad to 8 characters
491             {
492                 hexStr = "0" + hexStr;
493             }
494 
495             // place the inverted sub authorities, 4 bytes each
496             for ( int j = hexStr.length(); j > 0; j = j - 2)
497             {
498                 sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(j-2, j),16);
499             }
500         }
501 
502         return sidBytes;
503     }
504 }
505