001package serp.util; 002 003import java.math.*; 004import java.util.*; 005 006/** 007 * String utiltity methods. 008 * 009 * @author Abe White 010 */ 011public class Strings { 012 private static final Object[][] _codes = new Object[][] { 013 { byte.class, "byte", "B" }, 014 { char.class, "char", "C" }, 015 { double.class, "double", "D" }, 016 { float.class, "float", "F" }, 017 { int.class, "int", "I" }, 018 { long.class, "long", "J" }, 019 { short.class, "short", "S" }, 020 { boolean.class, "boolean", "Z" }, 021 { void.class, "void", "V" } 022 }; 023 024 /** 025 * Replace all instances of <code>from</code> in <code>str</code> 026 * with <code>to</code>. 027 * 028 * @param str the candidate string to replace 029 * @param from the token to replace 030 * @param to the new token 031 * @return the string with all the replacements made 032 */ 033 public static String replace(String str, String from, String to) { 034 String[] split = split(str, from, Integer.MAX_VALUE); 035 return join(split, to); 036 } 037 038 /** 039 * Splits the given string on the given token. Follows the semantics 040 * of the Java 1.4 {@link String#split(String,int)} method, but does 041 * not treat the given token as a regular expression. 042 */ 043 public static String[] split(String str, String token, int max) { 044 if (str == null || str.length() == 0) 045 return new String[0]; 046 if (token == null || token.length() == 0) 047 throw new IllegalArgumentException("token: [" + token + "]"); 048 049 // split on token 050 LinkedList ret = new LinkedList(); 051 int start = 0; 052 for (int split = 0; split != -1;) { 053 split = str.indexOf(token, start); 054 if (split == -1 && start >= str.length()) 055 ret.add(""); 056 else if (split == -1) 057 ret.add(str.substring(start)); 058 else { 059 ret.add(str.substring(start, split)); 060 start = split + token.length(); 061 } 062 } 063 064 // now take max into account; this isn't the most efficient way 065 // of doing things since we split the maximum number of times 066 // regardless of the given parameters, but it makes things easy 067 if (max == 0) { 068 // discard any trailing empty splits 069 while (ret.getLast().equals("")) 070 ret.removeLast(); 071 } else if (max > 0 && ret.size() > max) { 072 // move all splits over max into the last split 073 StringBuffer buf = new StringBuffer(ret.removeLast().toString()); 074 while (ret.size() >= max) { 075 buf.insert(0, token); 076 buf.insert(0, ret.removeLast()); 077 } 078 ret.add(buf.toString()); 079 } 080 return (String[]) ret.toArray(new String[ret.size()]); 081 } 082 083 /** 084 * Joins the given strings, placing the given token between them. 085 */ 086 public static String join(Object[] strings, String token) { 087 if (strings == null) 088 return null; 089 090 StringBuffer buf = new StringBuffer(20 * strings.length); 091 for (int i = 0; i < strings.length; i++) { 092 if (i > 0) 093 buf.append(token); 094 if (strings[i] != null) 095 buf.append(strings[i]); 096 } 097 return buf.toString(); 098 } 099 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}