View Javadoc

1   package serp.util;
2   
3   import java.math.*;
4   import java.util.*;
5   
6   /**
7    * String utiltity methods.
8    *
9    * @author Abe White
10   */
11  public class Strings {
12      private static final Object[][] _codes = new Object[][] {
13          { byte.class, "byte", "B" },
14          { char.class, "char", "C" },
15          { double.class, "double", "D" },
16          { float.class, "float", "F" },
17          { int.class, "int", "I" },
18          { long.class, "long", "J" },
19          { short.class, "short", "S" },
20          { boolean.class, "boolean", "Z" },
21          { void.class, "void", "V" }
22      };
23  
24      /**
25       * Replace all instances of <code>from</code> in <code>str</code>
26       * with <code>to</code>.
27       *
28       * @param str the candidate string to replace
29       * @param from the token to replace
30       * @param to the new token
31       * @return the string with all the replacements made
32       */
33      public static String replace(String str, String from, String to) {
34          String[] split = split(str, from, Integer.MAX_VALUE);
35          return join(split, to);
36      }
37  
38      /**
39       * Splits the given string on the given token. Follows the semantics
40       * of the Java 1.4 {@link String#split(String,int)} method, but does
41       * not treat the given token as a regular expression.
42       */
43      public static String[] split(String str, String token, int max) {
44          if (str == null || str.length() == 0)
45              return new String[0];
46          if (token == null || token.length() == 0)
47              throw new IllegalArgumentException("token: [" + token + "]");
48  
49          // split on token 
50          LinkedList ret = new LinkedList();
51          int start = 0;
52          for (int split = 0; split != -1;) {
53              split = str.indexOf(token, start);
54              if (split == -1 && start >= str.length())
55                  ret.add("");
56              else if (split == -1)
57                  ret.add(str.substring(start));
58              else {
59                  ret.add(str.substring(start, split));
60                  start = split + token.length();
61              }
62          }
63  
64          // now take max into account; this isn't the most efficient way
65          // of doing things since we split the maximum number of times
66          // regardless of the given parameters, but it makes things easy
67          if (max == 0) {
68              // discard any trailing empty splits
69              while (ret.getLast().equals(""))
70                  ret.removeLast();
71          } else if (max > 0 && ret.size() > max) {
72              // move all splits over max into the last split
73              StringBuffer buf = new StringBuffer(ret.removeLast().toString());
74              while (ret.size() >= max) {
75                  buf.insert(0, token);
76                  buf.insert(0, ret.removeLast());
77              }
78              ret.add(buf.toString());
79          }
80          return (String[]) ret.toArray(new String[ret.size()]);
81      }
82  
83      /**
84       * Joins the given strings, placing the given token between them.
85       */
86      public static String join(Object[] strings, String token) {
87          if (strings == null)
88              return null;
89  
90          StringBuffer buf = new StringBuffer(20 * strings.length);
91          for (int i = 0; i < strings.length; i++) {
92              if (i > 0)
93                  buf.append(token);
94              if (strings[i] != null)
95                  buf.append(strings[i]);
96          }
97          return buf.toString();
98      }
99  
100     /**
101      * Return the class for the given string, correctly handling
102      * primitive types. If the given class loader is null, the context
103      * loader of the current thread will be used.
104      *
105      * @throws RuntimeException on load error
106      */
107     public static Class toClass(String str, ClassLoader loader) {
108         return toClass(str, false, loader);
109     }
110 
111     /**
112      * Return the class for the given string, correctly handling
113      * primitive types. If the given class loader is null, the context
114      * loader of the current thread will be used.
115      *
116      * @throws RuntimeException on load error
117      */
118     public static Class toClass(String str, boolean resolve, 
119         ClassLoader loader) {
120         if (str == null)
121             throw new NullPointerException("str == null");
122 
123         // array handling
124         int dims = 0;
125         while (str.endsWith("[]")) {
126             dims++;
127             str = str.substring(0, str.length() - 2);
128         }
129 
130         // check against primitive types
131         boolean primitive = false;
132         if (str.indexOf('.') == -1) {
133             for (int i = 0; !primitive && (i < _codes.length); i++) {
134                 if (_codes[i][1].equals(str)) {
135                     if (dims == 0)
136                         return (Class) _codes[i][0];
137                     str = (String) _codes[i][2];
138                     primitive = true;
139                 }
140             }
141         }
142 
143         if (dims > 0) {
144             int size = str.length() + dims;
145             if (!primitive)
146                 size += 2;
147 
148             StringBuffer buf = new StringBuffer(size);
149             for (int i = 0; i < dims; i++)
150                 buf.append('[');
151             if (!primitive)
152                 buf.append('L');
153             buf.append(str);
154             if (!primitive)
155                 buf.append(';');
156             str = buf.toString();
157         }
158 
159         if (loader == null)
160             loader = Thread.currentThread().getContextClassLoader();
161         try {
162             return Class.forName(str, resolve, loader);
163         } catch (Throwable t) {
164             throw new IllegalArgumentException(t.toString());
165         }
166     }
167 
168     /**
169      * Return only the class name, without package.
170      */
171     public static String getClassName(Class cls) {
172         return (cls == null) ? null : getClassName(cls.getName());
173     }
174 
175     /**
176      * Return only the class name.
177      */
178     public static String getClassName(String fullName) {
179         if (fullName == null)
180             return null;
181 
182         // special case for arrays
183         int dims = 0;
184         for (int i = 0; i < fullName.length(); i++) {
185             if (fullName.charAt(i) != '[') {
186                 dims = i;
187                 break;
188             }
189         }
190         if (dims > 0)
191             fullName = fullName.substring(dims);
192 
193         // check for primitives
194         for (int i = 0; i < _codes.length; i++) {
195             if (_codes[i][2].equals(fullName)) {
196                 fullName = (String) _codes[i][1];
197                 break;
198             }
199         }
200 
201         fullName = fullName.substring(fullName.lastIndexOf('.') + 1);
202         for (int i = 0; i < dims; i++)
203             fullName = fullName + "[]";
204         return fullName;
205     }
206 
207     /**
208      * Return only the package, or empty string if none.
209      */
210     public static String getPackageName(Class cls) {
211         return (cls == null) ? null : getPackageName(cls.getName());
212     }
213 
214     /**
215      * Return only the package, or empty string if none.
216      */
217     public static String getPackageName(String fullName) {
218         if (fullName == null)
219             return null;
220         int dotIdx = fullName.lastIndexOf('.');
221         return (dotIdx == -1) ? "" : fullName.substring(0, dotIdx);
222     }
223 
224     /**
225      * Return <code>val</code> as the type specified by
226      * <code>type</code>. If <code>type</code> is a primitive, the
227      * primitive wrapper type is created and returned, and
228      * <code>null</code>s are converted to the Java default for the
229      * primitive type.
230      *
231      * @param val The string value to parse
232      * @param type The type to parse. This must be a primitive or a
233      * primitive wrapper, or one of {@link BigDecimal},
234      * {@link BigInteger}, {@link String}, {@link Date}.
235      * @throws IllegalArgumentException if <code>type</code> is not a
236      * supported type, or if <code>val</code> cannot be
237      * converted into an instance of type <code>type</code>.
238      */
239     public static Object parse(String val, Class type) {
240         if (!canParse(type))
241             throw new IllegalArgumentException("invalid type: " +
242                 type.getName());
243 
244         // deal with null value
245         if (val == null) {
246             if (!type.isPrimitive())
247                 return null;
248             if (type == boolean.class)
249                 return Boolean.FALSE;
250             if (type == byte.class)
251                 return new Byte((byte) 0);
252             if (type == char.class)
253                 return new Character((char) 0);
254             if (type == double.class)
255                 return new Double(0);
256             if (type == float.class)
257                 return new Float(0);
258             if (type == int.class)
259                 return Numbers.valueOf(0);
260             if (type == long.class)
261                 return Numbers.valueOf(0L);
262             if (type == short.class)
263                 return new Short((short) 0);
264             throw new IllegalStateException("invalid type: " + type);
265         }
266 
267         // deal with non-null value
268         if (type == boolean.class || type == Boolean.class)
269             return Boolean.valueOf(val);
270         if (type == byte.class || type == Byte.class)
271             return Byte.valueOf(val);
272         if (type == char.class || type == Character.class) {
273             if (val.length() == 0)
274                 return new Character((char) 0);
275             if (val.length() == 1)
276                 return new Character(val.charAt(0));
277             throw new IllegalArgumentException("'" + val + "' is longer than " 
278                 + "one character.");
279         }
280         if (type == double.class || type == Double.class)
281             return Double.valueOf(val);
282         if (type == float.class || type == Float.class)
283             return Float.valueOf(val);
284         if (type == int.class || type == Integer.class)
285             return Integer.valueOf(val);
286         if (type == long.class || type == Long.class)
287             return Long.valueOf(val);
288         if (type == short.class || type == Short.class)
289             return Short.valueOf(val);
290         if (type == String.class)
291             return val;
292         if (type == Date.class)
293             return new Date(val);
294         if (type == BigInteger.class)
295             return new BigInteger(val);
296         if (type == BigDecimal.class)
297             return new BigDecimal(val);
298         throw new IllegalArgumentException("Invalid type: " + type);
299     }
300 
301     /**
302      * Whether the given type is parsable via {@link #parse}.
303      */
304     public static boolean canParse(Class type) {
305         return type.isPrimitive() || type == Boolean.class 
306             || type == Byte.class || type == Character.class 
307             || type == Short.class || type == Integer.class 
308             || type == Long.class || type == Float.class 
309             || type == Double.class || type == String.class 
310             || type == Date.class || type == BigInteger.class 
311             || type == BigDecimal.class;
312     }
313 }