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