001: // Utils - assorted static utility routines
002: //
003: // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions
007: // are met:
008: // 1. Redistributions of source code must retain the above copyright
009: // notice, this list of conditions and the following disclaimer.
010: // 2. Redistributions in binary form must reproduce the above copyright
011: // notice, this list of conditions and the following disclaimer in the
012: // documentation and/or other materials provided with the distribution.
013: //
014: // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
015: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
016: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
017: // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
018: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
019: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
020: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
021: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
022: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
023: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
024: // SUCH DAMAGE.
025: //
026: // Visit the ACME Labs Java page for up-to-date versions of this and other
027: // fine Java utilities: http://www.acme.com/java/
028:
029: package Acme;
030:
031: import java.util.*;
032: import java.io.*;
033: import java.net.*;
034: import java.text.SimpleDateFormat;
035:
036: /// Assorted static utility routines.
037: // <P>
038: // Whenever I come up with a static routine that might be of general use,
039: // I put it here. So far the class includes:
040: // <UL>
041: // <LI> some string routines that were left out of java.lang.String
042: // <LI> a general array-to-string routine
043: // <LI> a fixed version of java.io.InputStream's byte-array read routine
044: // <LI> a bunch of URL-hacking routines
045: // <LI> some easy-to-use wrappers for Runtime.exec
046: // <LI> a debugging routine to dump the current call stack
047: // <LI> a URLDecoder to match java.net.URLEncoder
048: // </UL>
049: // and lots more.
050: // <P>
051: // <A HREF="/resources/classes/Acme/Utils.java">Fetch the software.</A><BR>
052: // <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
053:
054: public class Utils {
055:
056: /// Returns a date string formatted in Unix ls style - if it's within
057: // six months of now, Mmm dd hh:ss, else Mmm dd yyyy.
058: static final SimpleDateFormat shortfmt = new SimpleDateFormat(
059: "MMM dd HH:mm");
060: static final SimpleDateFormat longfmt = new SimpleDateFormat(
061: "MMM dd yyyy");
062:
063: public static String lsDateStr(Date date) {
064: if (Math.abs(System.currentTimeMillis() - date.getTime()) < 183L
065: * 24L * 60L * 60L * 1000L)
066: return shortfmt.format(date);
067: else
068: return longfmt.format(date);
069: }
070:
071: /// Returns "s" for numbers other than one, and "" for one.
072: public static String pluralStr(long n) {
073: if (n == 1)
074: return "";
075: else
076: return "s";
077: }
078:
079: // Various interval constants. Some are only approximate.
080: public static final long INT_SECOND = 1000L;
081: public static final long INT_MINUTE = INT_SECOND * 60L;
082: public static final long INT_HOUR = INT_MINUTE * 60L;
083: public static final long INT_DAY = INT_HOUR * 24L;
084: public static final long INT_WEEK = INT_DAY * 7L;
085: public static final long INT_MONTH = INT_DAY * 30L;
086: public static final long INT_YEAR = INT_DAY * 365L;
087: public static final long INT_DECADE = INT_DAY * 3652L;
088:
089: /// Returns a string approximately describing a given time interval.
090: // @param interval the interval, in milliseconds
091: public static String intervalStr(long interval) {
092: long decades, years, months, weeks, days, hours, minutes, seconds, millis;
093:
094: decades = interval / INT_DECADE;
095: interval -= decades * INT_DECADE;
096: years = interval / INT_YEAR;
097: interval -= years * INT_YEAR;
098: months = interval / INT_MONTH;
099: interval -= months * INT_MONTH;
100: weeks = interval / INT_WEEK;
101: interval -= weeks * INT_WEEK;
102: days = interval / INT_DAY;
103: interval -= days * INT_DAY;
104: hours = interval / INT_HOUR;
105: interval -= hours * INT_HOUR;
106: minutes = interval / INT_MINUTE;
107: interval -= minutes * INT_MINUTE;
108: seconds = interval / INT_SECOND;
109: interval -= seconds * INT_SECOND;
110: millis = interval;
111:
112: if (decades > 0)
113: if (years == 0)
114: return decades + " decade" + pluralStr(decades);
115: else
116: return decades + " decade" + pluralStr(decades) + ", "
117: + years + " years" + pluralStr(years);
118: else if (years > 0)
119: if (months == 0)
120: return years + " year" + pluralStr(years);
121: else
122: return years + " year" + pluralStr(years) + ", "
123: + months + " month" + pluralStr(months);
124: else if (months > 0)
125: if (weeks == 0)
126: return months + " month" + pluralStr(months);
127: else
128: return months + " month" + pluralStr(months) + ", "
129: + weeks + " week" + pluralStr(weeks);
130: else if (weeks > 0)
131: if (days == 0)
132: return weeks + " week" + pluralStr(weeks);
133: else
134: return weeks + " week" + pluralStr(weeks) + ", " + days
135: + " day" + pluralStr(days);
136: else if (days > 0)
137: if (hours == 0)
138: return days + " day" + pluralStr(days);
139: else
140: return days + " day" + pluralStr(days) + ", " + hours
141: + " hour" + pluralStr(hours);
142: else if (hours > 0)
143: if (minutes == 0)
144: return hours + " hour" + pluralStr(hours);
145: else
146: return hours + " hour" + pluralStr(hours) + ", "
147: + minutes + " minute" + pluralStr(minutes);
148: else if (minutes > 0)
149: if (seconds == 0)
150: return minutes + " minute" + pluralStr(minutes);
151: else
152: return minutes + " minute" + pluralStr(minutes) + ", "
153: + seconds + " second" + pluralStr(seconds);
154: else if (seconds > 0)
155: if (millis == 0)
156: return seconds + " second" + pluralStr(seconds);
157: else
158: return seconds + " second" + pluralStr(seconds) + ", "
159: + millis + " millisecond" + pluralStr(millis);
160: else
161: return millis + " millisecond" + pluralStr(millis);
162: }
163:
164: /// Returns the length of the initial segment of str which consists
165: // entirely of characters from charSet.
166: public static int strSpan(String str, String charSet) {
167: return strSpan(str, charSet, 0);
168: }
169:
170: /// Returns the length of the initial segment of str which consists
171: // entirely of characters from charSet, starting at the given index.
172: public static int strSpan(String str, String charSet, int fromIdx) {
173: int i;
174: for (i = fromIdx; i < str.length(); ++i)
175: if (charSet.indexOf(str.charAt(i)) == -1)
176: break;
177: return i - fromIdx;
178: }
179:
180: /// Returns the length of the initial segment of str which consists
181: // entirely of characters NOT from charSet.
182: public static int strCSpan(String str, String charSet) {
183: return strCSpan(str, charSet, 0);
184: }
185:
186: /// Returns the length of the initial segment of str which consists
187: // entirely of characters NOT from charSet, starting at the given index.
188: public static int strCSpan(String str, String charSet, int fromIdx) {
189: int i;
190: for (i = fromIdx; i < str.length(); ++i)
191: if (charSet.indexOf(str.charAt(i)) != -1)
192: break;
193: return i - fromIdx;
194: }
195:
196: /// Checks whether a string matches a given wildcard pattern.
197: // Only does ? and *, and multiple patterns separated by |.
198: public static boolean match(String pattern, String string) {
199: for (int p = 0;; ++p) {
200: for (int s = 0;; ++p, ++s) {
201: boolean sEnd = (s >= string.length());
202: boolean pEnd = (p >= pattern.length() || pattern
203: .charAt(p) == '|');
204: if (sEnd && pEnd)
205: return true;
206: if (sEnd || pEnd)
207: break;
208: if (pattern.charAt(p) == '?')
209: continue;
210: if (pattern.charAt(p) == '*') {
211: int i;
212: ++p;
213: for (i = string.length(); i >= s; --i)
214: if (match(pattern.substring(p), string
215: .substring(i))) /* not quite right */
216: return true;
217: break;
218: }
219: if (pattern.charAt(p) != string.charAt(s))
220: break;
221: }
222: p = pattern.indexOf('|', p);
223: if (p == -1)
224: return false;
225: }
226: }
227:
228: /// Finds the maximum length of a string that matches a given wildcard
229: // pattern. Only does ? and *, and multiple patterns separated by |.
230: public static int matchSpan(String pattern, String string) {
231: int result = 0;
232: StringTokenizer st = new StringTokenizer(pattern, "|");
233:
234: while (st.hasMoreTokens()) {
235: int len = matchSpan1(st.nextToken(), string);
236: if (len > result)
237: result = len;
238: }
239: return result;
240: }
241:
242: static int matchSpan1(String pattern, String string) {
243: int p = 0;
244: for (; p < string.length() && p < pattern.length(); p++) {
245: if (pattern.charAt(p) == string.charAt(p))
246: continue;
247: if (pattern.charAt(p) == '*')
248: return p - 1;
249: return 0;
250: }
251: return p < (pattern.length() - 1) ? -1 : p;
252: }
253:
254: /// Returns the length of the initial segment of str1 that equals str2.
255: public static int sameSpan(String str1, String str2) {
256: int i;
257: for (i = 0; i < str1.length() && i < str2.length()
258: && str1.charAt(i) == str2.charAt(i); ++i)
259: ;
260: return i;
261: }
262:
263: /// Returns the number of times the given character appears in the string.
264: public static int charCount(String str, char c) {
265: int n = 0;
266: for (int i = 0; i < str.length(); ++i)
267: if (str.charAt(i) == c)
268: ++n;
269: return n;
270: }
271:
272: /// Turns a String into an array of Strings, by using StringTokenizer
273: // to split it up at whitespace.
274: public static String[] splitStr(String str) {
275: StringTokenizer st = new StringTokenizer(str);
276: int n = st.countTokens();
277: String[] strs = new String[n];
278: for (int i = 0; i < n; ++i)
279: strs[i] = st.nextToken();
280: return strs;
281: }
282:
283: /// Turns a String into an array of Strings, by splitting it at
284: // the specified character. This does not use StringTokenizer,
285: // and therefore can handle empty fields.
286: public static String[] splitStr(String str, char delim) {
287: int n = 1;
288: int index = -1;
289: while (true) {
290: index = str.indexOf(delim, index + 1);
291: if (index == -1)
292: break;
293: ++n;
294: }
295: String[] strs = new String[n];
296: index = -1;
297: for (int i = 0; i < n - 1; ++i) {
298: int nextIndex = str.indexOf(delim, index + 1);
299: strs[i] = str.substring(index + 1, nextIndex);
300: index = nextIndex;
301: }
302: strs[n - 1] = str.substring(index + 1);
303: return strs;
304: }
305:
306: /// Turns an array of Strings into a single String, with the components
307: // separated by spaces.
308: public static String flattenStrarr(String[] strs) {
309: StringBuffer sb = new StringBuffer();
310: for (int i = 0; i < strs.length; ++i) {
311: if (i > 0)
312: sb.append(' ');
313: sb.append(strs[i]);
314: }
315: return sb.toString();
316: }
317:
318: /// Sorts an array of Strings.
319: // Java currently has no general sort function. Sorting Strings is
320: // common enough that it's worth making a special case.
321: public static void sortStrings(String[] strings) {
322: // Just does a bubblesort.
323: for (int i = 0; i < strings.length - 1; ++i) {
324: for (int j = i + 1; j < strings.length; ++j) {
325: if (strings[i].compareTo(strings[j]) > 0) {
326: String t = strings[i];
327: strings[i] = strings[j];
328: strings[j] = t;
329: }
330: }
331: }
332: }
333:
334: /// Locates a String in an array of Strings.
335: // Returns -1 if the String is not found.
336: public static int indexOfString(String[] strings, String string) {
337: for (int i = 0; i < strings.length; ++i)
338: if (string.equals(strings[i]))
339: return i;
340: return -1;
341: }
342:
343: /// Locates a String in an array of Strings, ignoring case.
344: // Returns -1 if the String is not found.
345: public static int indexOfStringIgnoreCase(String[] strings,
346: String string) {
347: for (int i = 0; i < strings.length; ++i)
348: if (string.equalsIgnoreCase(strings[i]))
349: return i;
350: return -1;
351: }
352:
353: /// Compares two arrays of Strings for equality.
354: public static boolean equalsStrings(String[] strings1,
355: String[] strings2) {
356: if (strings1.length != strings2.length)
357: return false;
358: for (int i = 0; i < strings1.length; ++i)
359: if (!strings1[i].equals(strings2[i]))
360: return false;
361: return true;
362: }
363:
364: /// Returns the number a raised to the power of b. Long version
365: // of Math.pow(). Throws ArithmeticException if b is negative.
366: public static long pow(long a, long b) throws ArithmeticException {
367: if (b < 0)
368: throw new ArithmeticException();
369: long r = 1;
370: while (b != 0) {
371: if (odd(b))
372: r *= a;
373: b >>>= 1;
374: a *= a;
375: }
376: return r;
377: }
378:
379: /// Parse an integer, returning a default value on errors.
380: public static int parseInt(String str, int def) {
381: try {
382: return Integer.parseInt(str);
383: } catch (Exception e) {
384: return def;
385: }
386: }
387:
388: /// Parse a long, returning a default value on errors.
389: public static long parseLong(String str, long def) {
390: try {
391: return Long.parseLong(str);
392: } catch (Exception e) {
393: return def;
394: }
395: }
396:
397: /// An array-to-String routine. Handles arrays of arbitrary
398: // type, including nested arrays. Sample output:
399: // <BLOCKQUOTE><CODE><PRE>
400: // byte[]: { (byte)0, (byte)1, (byte)2 }
401: // char[]: { '0', '1', '2' }
402: // short[]: { (short)0, (short)1, (short)2 }
403: // int[]: { 0, 1, 2 }
404: // long[]: { 0L, 1L, 2L }
405: // float[]: { 0F, 1F, 2F }
406: // double[]: { 0D, 1D, 2D }
407: // String[]: { "0", "1", "2" }
408: // int[][]: { { 0, 1, 2 }, { 3, 4, 5 } }
409: // </PRE></CODE></BLOCKQUOTE>
410: public static String arrayToString(Object o) {
411: if (o == null)
412: return "null";
413: String cl = o.getClass().getName();
414: if (!cl.startsWith("["))
415: // It's not an array; just call its toString method.
416: return o.toString();
417: StringBuffer sb = new StringBuffer("{ ");
418: if (o instanceof byte[]) {
419: byte[] ba = (byte[]) o;
420: for (int i = 0; i < ba.length; ++i) {
421: if (i > 0)
422: sb.append(", ");
423: sb.append("(byte)");
424: sb.append(ba[i]);
425: }
426: } else if (o instanceof char[]) {
427: char[] ca = (char[]) o;
428: for (int i = 0; i < ca.length; ++i) {
429: if (i > 0)
430: sb.append(", ");
431: sb.append("'");
432: sb.append(ca[i]);
433: sb.append("'");
434: }
435: } else if (o instanceof short[]) {
436: short[] sa = (short[]) o;
437: for (int i = 0; i < sa.length; ++i) {
438: if (i > 0)
439: sb.append(", ");
440: sb.append("(short)");
441: sb.append(sa[i]);
442: }
443: } else if (o instanceof int[]) {
444: int[] ia = (int[]) o;
445: for (int i = 0; i < ia.length; ++i) {
446: if (i > 0)
447: sb.append(", ");
448: sb.append(ia[i]);
449: }
450: } else if (o instanceof long[]) {
451: long[] la = (long[]) o;
452: for (int i = 0; i < la.length; ++i) {
453: if (i > 0)
454: sb.append(", ");
455: sb.append(la[i]);
456: sb.append("L");
457: }
458: } else if (o instanceof float[]) {
459: float[] fa = (float[]) o;
460: for (int i = 0; i < fa.length; ++i) {
461: if (i > 0)
462: sb.append(", ");
463: sb.append(fa[i]);
464: sb.append("F");
465: }
466: } else if (o instanceof double[]) {
467: double[] da = (double[]) o;
468: for (int i = 0; i < da.length; ++i) {
469: if (i > 0)
470: sb.append(", ");
471: sb.append(da[i]);
472: sb.append("D");
473: }
474: } else if (o instanceof String) {
475: // Special-case Strings so we can surround them with quotes.
476: String[] sa = (String[]) o;
477: for (int i = 0; i < sa.length; ++i) {
478: if (i > 0)
479: sb.append(", ");
480: sb.append("\"");
481: sb.append(sa[i]);
482: sb.append("\"");
483: }
484: } else if (cl.startsWith("[L")) {
485: // Some random class.
486: Object[] oa = (Object[]) o;
487: for (int i = 0; i < oa.length; ++i) {
488: if (i > 0)
489: sb.append(", ");
490: sb.append(oa[i]);
491: }
492: } else if (cl.startsWith("[[")) {
493: // Nested arrays.
494: Object[] aa = (Object[]) o;
495: for (int i = 0; i < aa.length; ++i) {
496: if (i > 0)
497: sb.append(", ");
498: sb.append(arrayToString(aa[i]));
499: }
500: } else
501: sb.append("(unknown array type)");
502: sb.append(" }");
503: return sb.toString();
504: }
505:
506: /// Check if an object extends a given class or one of its superclasses.
507: // An instanceof that works on Class objects at runtime, instead
508: // of type descriptors at compile time.
509: public static boolean instanceOf(Object o, Class cl) {
510: // Null check.
511: if (o == null || cl == null)
512: return false;
513: Class ocl = o.getClass();
514: // Check if they are the same class.
515: if (ocl.equals(cl))
516: return true;
517: // If the class is not itself an interface, then check its interfaces.
518: if (!cl.isInterface()) {
519: Class ifs[] = cl.getInterfaces();
520: for (int i = 0; i < ifs.length; ++i)
521: if (instanceOf(o, ifs[i]))
522: return true;
523: }
524: // And check supeclasses.
525: Class scl = cl.getSuperclass();
526: if (scl != null)
527: if (instanceOf(o, scl))
528: return true;
529: // Guess not.
530: return false;
531: }
532:
533: /// Test is a number is even.
534: public static boolean even(long n) {
535: return (n & 1) == 0;
536: }
537:
538: /// Test is a number is odd.
539: public static boolean odd(long n) {
540: return (n & 1) != 0;
541: }
542:
543: /// Count the number of 1-bits in a byte.
544: public static int countOnes(byte n) {
545: return countOnes(n & 0xffL);
546: }
547:
548: /// Count the number of 1-bits in an int.
549: public static int countOnes(int n) {
550: return countOnes(n & 0xffffffffL);
551: }
552:
553: /// Count the number of 1-bits in a long.
554: public static int countOnes(long n) {
555: // There are faster ways to do this, all the way up to looking
556: // up bytes in a 256-element table. But this is not too bad.
557: int count = 0;
558: while (n != 0) {
559: if (odd(n))
560: ++count;
561: n >>>= 1;
562: }
563: return count;
564: }
565:
566: /// A fixed version of java.io.InputStream.read(byte[], int, int). The
567: // standard version catches and ignores IOExceptions from below.
568: // This version sends them on to the caller.
569: public static int read(InputStream in, byte[] b, int off, int len)
570: throws IOException {
571: if (len <= 0)
572: return 0;
573: int c = in.read();
574: if (c == -1)
575: return -1;
576: if (b != null)
577: b[off] = (byte) c;
578: int i;
579: for (i = 1; i < len; ++i) {
580: c = in.read();
581: if (c == -1)
582: break;
583: if (b != null)
584: b[off + i] = (byte) c;
585: }
586: return i;
587: }
588:
589: /// A version of read that reads the entire requested block, instead
590: // of sometimes terminating early.
591: // @return -1 on EOF, otherwise len
592: public static int readFully(InputStream in, byte[] b, int off,
593: int len) throws IOException {
594: int l, r;
595: for (l = 0; l < len;) {
596: r = read(in, b, l, len - l);
597: if (r == -1)
598: return -1;
599: l += r;
600: }
601: return len;
602: }
603:
604: /// Make a URL with no ref part and no query string. Also, if it's
605: // a directory then make sure there's a trailing slash.
606: public static URL plainUrl(URL context, String urlStr)
607: throws MalformedURLException {
608: URL url = new URL(context, urlStr);
609: String fileStr = url.getFile();
610: int i = fileStr.indexOf('?');
611: if (i != -1)
612: fileStr = fileStr.substring(0, i);
613: url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
614: fileStr);
615: if ((!fileStr.endsWith("/"))
616: && urlStrIsDir(url.toExternalForm())) {
617: fileStr = fileStr + "/";
618: url = new URL(url.getProtocol(), url.getHost(), url
619: .getPort(), fileStr);
620: }
621: return url;
622: }
623:
624: /// Make a URL with no ref part and no query string. Also, if it's
625: // a directory then make sure there's a trailing slash.
626: public static URL plainUrl(String urlStr)
627: throws MalformedURLException {
628: return plainUrl(null, urlStr);
629: }
630:
631: /// Figure out the base URL for a given URL. What this means is
632: // if the URL points to a directory, you get that directory; if the
633: // URL points to a file, you get the directory the file is in.
634: public static String baseUrlStr(String urlStr) {
635: if (urlStr.endsWith("/"))
636: return urlStr;
637: if (urlStrIsDir(urlStr))
638: return urlStr + "/";
639: return urlStr.substring(0, urlStr.lastIndexOf('/') + 1);
640: }
641:
642: /// Makes sure if a URL is a directory, it ends with a slash.
643: public static String fixDirUrlStr(String urlStr) {
644: if (urlStr.endsWith("/"))
645: return urlStr;
646: if (urlStrIsDir(urlStr))
647: return urlStr + "/";
648: return urlStr;
649: }
650:
651: /// Figures out whether a URL points to a directory or not.
652: // Web servers are lenient and accept directory-URLs without
653: // the trailing slash. What they actually do is return a
654: // redirect to the same URL with the trailing slash appended.
655: // Unfortunately, Java doesn't let us see that such a redirect
656: // happened. Instead we have to figure out it's a directory
657: // indirectly and heuristically.
658: public static boolean urlStrIsDir(String urlStr) {
659: // If it ends with a slash, it's probably a directory.
660: if (urlStr.endsWith("/"))
661: return true;
662:
663: // If the last component has a dot, it's probably not a directory.
664: int lastSlash = urlStr.lastIndexOf('/');
665: int lastPeriod = urlStr.lastIndexOf('.');
666: if (lastPeriod != -1
667: && (lastSlash == -1 || lastPeriod > lastSlash))
668: return false;
669:
670: // Otherwise, append a slash and try to connect. This is
671: // fairly expensive.
672: String urlStrWithSlash = urlStr + "/";
673: try {
674: URL url = new URL(urlStrWithSlash);
675: InputStream f = url.openStream();
676: f.close();
677: // Worked fine - it's probably a directory.
678: return true;
679: } catch (Exception e) {
680: // Got an error - must not be a directory.
681: return false;
682: }
683: }
684:
685: // Figures out whether a URL is absolute or not.
686: public static boolean urlStrIsAbsolute(String urlStr) {
687: if (urlStr.startsWith("/") || urlStr.indexOf(":/") != -1)
688: return true;
689: // Should handle :8000/ and such too.
690: return false;
691: }
692:
693: // Returns an equivalent URL string that is guaranteed to be absolute.
694: public static String absoluteUrlStr(String urlStr, URL contextUrl)
695: throws MalformedURLException {
696: URL url = new URL(contextUrl, urlStr);
697: return url.toExternalForm();
698: }
699:
700: /// URLDecoder to go along with java.net.URLEncoder. Why there isn't
701: // already a decoder in the standard library is a mystery to me.
702: public static String urlDecoder(String encoded) {
703: StringBuffer decoded = new StringBuffer();
704: int len = encoded.length();
705: for (int i = 0; i < len; ++i) {
706: if (encoded.charAt(i) == '%' && i + 2 < len) {
707: int d1 = Character.digit(encoded.charAt(i + 1), 16);
708: int d2 = Character.digit(encoded.charAt(i + 2), 16);
709: if (d1 != -1 && d2 != -1)
710: decoded.append((char) ((d1 << 4) + d2));
711: i += 2;
712: } else if (encoded.charAt(i) == '+')
713: decoded.append(' ');
714: else
715: decoded.append(encoded.charAt(i));
716: }
717: return decoded.toString();
718: }
719:
720: /// Check if an array contains a given element.
721: public static boolean arraycontains(Object[] array, Object element) {
722: for (int i = 0; i < array.length; ++i)
723: if (array[i].equals(element))
724: return true;
725: return false;
726: }
727:
728: /// Run a program on the host system.
729: // <P>
730: // This routine runs the specified command, waits for it to
731: // finish, and returns the exit status.
732: // This is like the Unix system() routine. Unlike the Unix version,
733: // though, stdout and stderr get thrown away unless you redirect them.
734: public static int system(String cmd) {
735: try {
736: return runCommand(cmd).waitFor();
737: } catch (IOException e) {
738: return -1;
739: } catch (InterruptedException e) {
740: return -1;
741: }
742: }
743:
744: /// Run a program on the host system, and capture the output.
745: // <P>
746: // This routine runs the specified command, and returns an InputStream
747: // for reading the output of the program.
748: // <P>
749: // <B>WARNING:</B> In JDK1.0.2 there is a serious bug in the process
750: // IO routines, such that reading all the way to the end of a process's
751: // output will invariably get you an IOException( "read error" ).
752: // In some cases you will also <B>lose</B> the last bufferload of
753: // the output. The workaround is to add a " ; sleep 1" to the end of
754: // your command, and to ignore the "read error" IOException.
755: public static InputStream popenr(String cmd) {
756: try {
757: return runCommand(cmd).getInputStream();
758: } catch (IOException e) {
759: return null;
760: }
761: }
762:
763: /// Run a program on the host system, and send it some input.
764: // <P>
765: // This routine runs the specified command, and returns an OutputStream
766: // for writing the program's input.
767: public static OutputStream popenw(String cmd) {
768: try {
769: return runCommand(cmd).getOutputStream();
770: } catch (IOException e) {
771: return null;
772: }
773: }
774:
775: /// Run a program on the host system.
776: // <P>
777: // This routine runs the specified command, and returns a Process
778: // object so you can do what you like with it.
779: // <P>
780: // <B>WARNING:</B> In JDK1.0.2 there is a serious bug in the process
781: // IO routines, such that reading all the way to the end of a process's
782: // output will invariably get you an IOException( "read error" ).
783: // In some cases you will also <B>lose</B> the last bufferload of
784: // the output. The workaround is to add a " ; sleep 1" to the end of
785: // your command, and to ignore the "read error" IOException.
786: public static Process runCommand(String cmd) throws IOException {
787: Runtime runtime = Runtime.getRuntime();
788: String[] shCmd = new String[3];
789: shCmd[0] = "/bin/sh";
790: shCmd[1] = "-c";
791: shCmd[2] = cmd;
792: return runtime.exec(shCmd);
793: }
794:
795: /// Copy the input to the output until EOF.
796: public static void copyStream(InputStream in, OutputStream out)
797: throws IOException {
798: byte[] buf = new byte[4096];
799: int len;
800: while ((len = in.read(buf)) != -1)
801: out.write(buf, 0, len);
802: }
803:
804: /// Copy the input to the output until EOF.
805: public static void copyStream(Reader in, Writer out)
806: throws IOException {
807: char[] buf = new char[4096];
808: int len;
809: while ((len = in.read(buf)) != -1)
810: out.write(buf, 0, len);
811: }
812:
813: /// Copy the input to the output until EOF.
814: public static void copyStream(InputStream in, Writer out)
815: throws IOException {
816: byte[] buf1 = new byte[4096];
817: char[] buf2 = new char[4096];
818: int len, i;
819: while ((len = in.read(buf1)) != -1) {
820: for (i = 0; i < len; ++i)
821: buf2[i] = (char) buf1[i];
822: out.write(buf2, 0, len);
823: }
824: }
825:
826: /// Copy the input to the output until EOF.
827: public static void copyStream(Reader in, OutputStream out)
828: throws IOException {
829: char[] buf1 = new char[4096];
830: byte[] buf2 = new byte[4096];
831: int len, i;
832: while ((len = in.read(buf1)) != -1) {
833: for (i = 0; i < len; ++i)
834: buf2[i] = (byte) buf1[i];
835: out.write(buf2, 0, len);
836: }
837: }
838:
839: /// Dump out the current call stack.
840: public static void dumpStack(PrintStream p) {
841: (new Throwable()).printStackTrace(p);
842: }
843:
844: /// Dump out the current call stack onto System.err.
845: public static void dumpStack() {
846: (new Throwable()).printStackTrace();
847: }
848:
849: /// add a hash table to a hash table
850: public static void putAll(Hashtable _dest, Hashtable _src) {
851: }
852:
853: }
|