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