View Javadoc

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