View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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       */
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     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
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      */
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      */
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      */
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      */
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      */
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     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
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      */
289     public static String nonNull(String s)
290     {
291         if (s==null)
292             return "";
293         return s;
294     }
295     
296     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
335     public static boolean isUTF8(String charset)
336     {
337         return __UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset);
338     }
339 
340 
341     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
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     
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     
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      */
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      */
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 }