001    package serp.util;
002    
003    import java.math.*;
004    import java.util.*;
005    
006    /**
007     * String utiltity methods.
008     *
009     * @author Abe White
010     */
011    public 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    }