0001: // Utils - assorted static utility routines
0002: //
0003: // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
0004: //
0005: // Redistribution and use in source and binary forms, with or without
0006: // modification, are permitted provided that the following conditions
0007: // are met:
0008: // 1. Redistributions of source code must retain the above copyright
0009: // notice, this list of conditions and the following disclaimer.
0010: // 2. Redistributions in binary form must reproduce the above copyright
0011: // notice, this list of conditions and the following disclaimer in the
0012: // documentation and/or other materials provided with the distribution.
0013: //
0014: // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017: // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024: // SUCH DAMAGE.
0025: //
0026: // Visit the ACME Labs Java page for up-to-date versions of this and other
0027: // fine Java utilities: http://www.acme.com/java/
0028:
0029: package Acme;
0030:
0031: import java.util.*;
0032: import java.io.*;
0033: import java.net.*;
0034:
0035: /// Assorted static utility routines.
0036: // <P>
0037: // Whenever I come up with a static routine that might be of general use,
0038: // I put it here. So far the class includes:
0039: // <UL>
0040: // <LI> some string routines that were left out of java.lang.String
0041: // <LI> a general array-to-string routine
0042: // <LI> a fixed version of java.io.InputStream's byte-array read routine
0043: // <LI> a bunch of URL-hacking routines
0044: // <LI> some easy-to-use wrappers for Runtime.exec
0045: // <LI> a debugging routine to dump the current call stack
0046: // <LI> a URLDecoder to match java.net.URLEncoder
0047: // </UL>
0048: // and lots more.
0049: // <P>
0050: // <A HREF="/resources/classes/Acme/Utils.java">Fetch the software.</A><BR>
0051: // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
0052:
0053: public class Utils {
0054:
0055: /// Returns a date string formatted in Unix ls style - if it's within
0056: // six months of now, Mmm dd hh:ss, else Mmm dd yyyy.
0057: public static String lsDateStr(Date date) {
0058: Calendar cal = new GregorianCalendar();
0059: cal.setTime(date);
0060: long dateTime = date.getTime();
0061: if (dateTime == -1L)
0062: return "------------";
0063: long nowTime = (new Date()).getTime();
0064: String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
0065: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
0066: String part1 = months[cal.get(Calendar.MONTH)]
0067: + Fmt.fmt(cal.get(Calendar.DATE), 3);
0068: if (Math.abs(nowTime - dateTime) < 183L * 24L * 60L * 60L
0069: * 1000L)
0070: return part1 + Fmt.fmt(cal.get(Calendar.HOUR_OF_DAY), 3)
0071: + ":"
0072: + Fmt.fmt(cal.get(Calendar.MINUTE), 2, Fmt.ZF);
0073: else
0074: return part1 + Fmt.fmt(cal.get(Calendar.YEAR), 6);
0075: }
0076:
0077: /// Returns "s" for numbers other than one, and "" for one.
0078: public static String pluralStr(long n) {
0079: if (n == 1)
0080: return "";
0081: else
0082: return "s";
0083: }
0084:
0085: // Various interval constants. Some are only approximate.
0086: public static final long INT_SECOND = 1000L;
0087: public static final long INT_MINUTE = INT_SECOND * 60L;
0088: public static final long INT_HOUR = INT_MINUTE * 60L;
0089: public static final long INT_DAY = INT_HOUR * 24L;
0090: public static final long INT_WEEK = INT_DAY * 7L;
0091: public static final long INT_MONTH = INT_DAY * 30L;
0092: public static final long INT_YEAR = INT_DAY * 365L;
0093: public static final long INT_DECADE = INT_DAY * 3652L;
0094:
0095: /// Returns a string approximately describing a given time interval.
0096: // @param interval the interval, in milliseconds
0097: public static String intervalStr(long interval) {
0098: long decades, years, months, weeks, days, hours, minutes, seconds, millis;
0099:
0100: decades = interval / INT_DECADE;
0101: interval -= decades * INT_DECADE;
0102: years = interval / INT_YEAR;
0103: interval -= years * INT_YEAR;
0104: months = interval / INT_MONTH;
0105: interval -= months * INT_MONTH;
0106: weeks = interval / INT_WEEK;
0107: interval -= weeks * INT_WEEK;
0108: days = interval / INT_DAY;
0109: interval -= days * INT_DAY;
0110: hours = interval / INT_HOUR;
0111: interval -= hours * INT_HOUR;
0112: minutes = interval / INT_MINUTE;
0113: interval -= minutes * INT_MINUTE;
0114: seconds = interval / INT_SECOND;
0115: interval -= seconds * INT_SECOND;
0116: millis = interval;
0117:
0118: if (decades > 0)
0119: if (years == 0)
0120: return decades + " decade" + pluralStr(decades);
0121: else
0122: return decades + " decade" + pluralStr(decades) + ", "
0123: + years + " years" + pluralStr(years);
0124: else if (years > 0)
0125: if (months == 0)
0126: return years + " year" + pluralStr(years);
0127: else
0128: return years + " year" + pluralStr(years) + ", "
0129: + months + " month" + pluralStr(months);
0130: else if (months > 0)
0131: if (weeks == 0)
0132: return months + " month" + pluralStr(months);
0133: else
0134: return months + " month" + pluralStr(months) + ", "
0135: + weeks + " week" + pluralStr(weeks);
0136: else if (weeks > 0)
0137: if (days == 0)
0138: return weeks + " week" + pluralStr(weeks);
0139: else
0140: return weeks + " week" + pluralStr(weeks) + ", " + days
0141: + " day" + pluralStr(days);
0142: else if (days > 0)
0143: if (hours == 0)
0144: return days + " day" + pluralStr(days);
0145: else
0146: return days + " day" + pluralStr(days) + ", " + hours
0147: + " hour" + pluralStr(hours);
0148: else if (hours > 0)
0149: if (minutes == 0)
0150: return hours + " hour" + pluralStr(hours);
0151: else
0152: return hours + " hour" + pluralStr(hours) + ", "
0153: + minutes + " minute" + pluralStr(minutes);
0154: else if (minutes > 0)
0155: if (seconds == 0)
0156: return minutes + " minute" + pluralStr(minutes);
0157: else
0158: return minutes + " minute" + pluralStr(minutes) + ", "
0159: + seconds + " second" + pluralStr(seconds);
0160: else if (seconds > 0)
0161: if (millis == 0)
0162: return seconds + " second" + pluralStr(seconds);
0163: else
0164: return seconds + " second" + pluralStr(seconds) + ", "
0165: + millis + " millisecond" + pluralStr(millis);
0166: else
0167: return millis + " millisecond" + pluralStr(millis);
0168: }
0169:
0170: /// Returns the length of the initial segment of str which consists
0171: // entirely of characters from charSet.
0172: public static int strSpan(String str, String charSet) {
0173: return strSpan(str, charSet, 0);
0174: }
0175:
0176: /// Returns the length of the initial segment of str which consists
0177: // entirely of characters from charSet, starting at the given index.
0178: public static int strSpan(String str, String charSet, int fromIdx) {
0179: int i;
0180: for (i = fromIdx; i < str.length(); ++i)
0181: if (charSet.indexOf(str.charAt(i)) == -1)
0182: break;
0183: return i - fromIdx;
0184: }
0185:
0186: /// Returns the length of the initial segment of str which consists
0187: // entirely of characters NOT from charSet.
0188: public static int strCSpan(String str, String charSet) {
0189: return strCSpan(str, charSet, 0);
0190: }
0191:
0192: /// Returns the length of the initial segment of str which consists
0193: // entirely of characters NOT from charSet, starting at the given index.
0194: public static int strCSpan(String str, String charSet, int fromIdx) {
0195: int i;
0196: for (i = fromIdx; i < str.length(); ++i)
0197: if (charSet.indexOf(str.charAt(i)) != -1)
0198: break;
0199: return i - fromIdx;
0200: }
0201:
0202: /// Checks whether a string matches a given wildcard pattern.
0203: // Only does ? and *, and multiple patterns separated by |.
0204: public static boolean match(String pattern, String string) {
0205: for (int p = 0;; ++p) {
0206: for (int s = 0;; ++p, ++s) {
0207: boolean sEnd = (s >= string.length());
0208: boolean pEnd = (p >= pattern.length() || pattern
0209: .charAt(p) == '|');
0210: if (sEnd && pEnd)
0211: return true;
0212: if (sEnd || pEnd)
0213: break;
0214: if (pattern.charAt(p) == '?')
0215: continue;
0216: if (pattern.charAt(p) == '*') {
0217: int i;
0218: ++p;
0219: for (i = string.length(); i >= s; --i)
0220: if (match(pattern.substring(p), string
0221: .substring(i))) /* not quite right */
0222: return true;
0223: break;
0224: }
0225: if (pattern.charAt(p) != string.charAt(s))
0226: break;
0227: }
0228: p = pattern.indexOf('|', p);
0229: if (p == -1)
0230: return false;
0231: }
0232: }
0233:
0234: // /// Finds the maximum length of a string that matches a given wildcard
0235: // // pattern. Only does ? and *, and multiple patterns separated by |.
0236: // public static int matchSpan( String pattern, String string )
0237: // {
0238: // // !!!
0239: // return 0;
0240: // }
0241:
0242: /// Returns the length of the initial segment of str1 that equals str2.
0243: public static int sameSpan(String str1, String str2) {
0244: int i;
0245: for (i = 0; i < str1.length() && i < str2.length()
0246: && str1.charAt(i) == str2.charAt(i); ++i)
0247: ;
0248: return i;
0249: }
0250:
0251: /// Returns the number of times the given character appears in the string.
0252: public static int charCount(String str, char c) {
0253: int n = 0;
0254: for (int i = 0; i < str.length(); ++i)
0255: if (str.charAt(i) == c)
0256: ++n;
0257: return n;
0258: }
0259:
0260: /// Turns a String into an array of Strings, by using StringTokenizer
0261: // to split it up at whitespace.
0262: public static String[] splitStr(String str) {
0263: StringTokenizer st = new StringTokenizer(str);
0264: int n = st.countTokens();
0265: String[] strs = new String[n];
0266: for (int i = 0; i < n; ++i)
0267: strs[i] = st.nextToken();
0268: return strs;
0269: }
0270:
0271: /// Turns a String into an array of Strings, by splitting it at
0272: // the specified character. This does not use StringTokenizer,
0273: // and therefore can handle empty fields.
0274: public static String[] splitStr(String str, char delim) {
0275: int n = 1;
0276: int index = -1;
0277: while (true) {
0278: index = str.indexOf(delim, index + 1);
0279: if (index == -1)
0280: break;
0281: ++n;
0282: }
0283: String[] strs = new String[n];
0284: index = -1;
0285: for (int i = 0; i < n - 1; ++i) {
0286: int nextIndex = str.indexOf(delim, index + 1);
0287: strs[i] = str.substring(index + 1, nextIndex);
0288: index = nextIndex;
0289: }
0290: strs[n - 1] = str.substring(index + 1);
0291: return strs;
0292: }
0293:
0294: /// Turns an array of Strings into a single String, with the components
0295: // separated by spaces.
0296: public static String flattenStrarr(String[] strs) {
0297: StringBuffer sb = new StringBuffer();
0298: for (int i = 0; i < strs.length; ++i) {
0299: if (i > 0)
0300: sb.append(' ');
0301: sb.append(strs[i]);
0302: }
0303: return sb.toString();
0304: }
0305:
0306: /// Sorts an array of Strings.
0307: // Java currently has no general sort function. Sorting Strings is
0308: // common enough that it's worth making a special case.
0309: public static void sortStrings(String[] strings) {
0310: // Just does a bubblesort.
0311: for (int i = 0; i < strings.length - 1; ++i) {
0312: for (int j = i + 1; j < strings.length; ++j) {
0313: if (strings[i].compareTo(strings[j]) > 0) {
0314: String t = strings[i];
0315: strings[i] = strings[j];
0316: strings[j] = t;
0317: }
0318: }
0319: }
0320: }
0321:
0322: /// Locates a String in an array of Strings.
0323: // Returns -1 if the String is not found.
0324: public static int indexOfString(String[] strings, String string) {
0325: for (int i = 0; i < strings.length; ++i)
0326: if (string.equals(strings[i]))
0327: return i;
0328: return -1;
0329: }
0330:
0331: /// Locates a String in an array of Strings, ignoring case.
0332: // Returns -1 if the String is not found.
0333: public static int indexOfStringIgnoreCase(String[] strings,
0334: String string) {
0335: for (int i = 0; i < strings.length; ++i)
0336: if (string.equalsIgnoreCase(strings[i]))
0337: return i;
0338: return -1;
0339: }
0340:
0341: /// Compares two arrays of Strings for equality.
0342: public static boolean equalsStrings(String[] strings1,
0343: String[] strings2) {
0344: if (strings1.length != strings2.length)
0345: return false;
0346: for (int i = 0; i < strings1.length; ++i)
0347: if (!strings1[i].equals(strings2[i]))
0348: return false;
0349: return true;
0350: }
0351:
0352: /// Returns the number a raised to the power of b. Long version
0353: // of Math.pow(). Throws ArithmeticException if b is negative.
0354: public static long pow(long a, long b) throws ArithmeticException {
0355: if (b < 0)
0356: throw new ArithmeticException();
0357: long r = 1;
0358: while (b != 0) {
0359: if (odd(b))
0360: r *= a;
0361: b >>>= 1;
0362: a *= a;
0363: }
0364: return r;
0365: }
0366:
0367: /// Parse an integer, returning a default value on errors.
0368: public static int parseInt(String str, int def) {
0369: try {
0370: return Integer.parseInt(str);
0371: } catch (Exception e) {
0372: return def;
0373: }
0374: }
0375:
0376: /// Parse a long, returning a default value on errors.
0377: public static long parseLong(String str, long def) {
0378: try {
0379: return Long.parseLong(str);
0380: } catch (Exception e) {
0381: return def;
0382: }
0383: }
0384:
0385: /// An array-to-String routine. Handles arrays of arbitrary
0386: // type, including nested arrays. Sample output:
0387: // <BLOCKQUOTE><CODE><PRE>
0388: // byte[]: { (byte)0, (byte)1, (byte)2 }
0389: // char[]: { '0', '1', '2' }
0390: // short[]: { (short)0, (short)1, (short)2 }
0391: // int[]: { 0, 1, 2 }
0392: // long[]: { 0L, 1L, 2L }
0393: // float[]: { 0F, 1F, 2F }
0394: // double[]: { 0D, 1D, 2D }
0395: // String[]: { "0", "1", "2" }
0396: // int[][]: { { 0, 1, 2 }, { 3, 4, 5 } }
0397: // </PRE></CODE></BLOCKQUOTE>
0398: public static String arrayToString(Object o) {
0399: if (o == null)
0400: return "null";
0401: String cl = o.getClass().getName();
0402: if (!cl.startsWith("["))
0403: // It's not an array; just call its toString method.
0404: return o.toString();
0405: StringBuffer sb = new StringBuffer("{ ");
0406: if (o instanceof byte[]) {
0407: byte[] ba = (byte[]) o;
0408: for (int i = 0; i < ba.length; ++i) {
0409: if (i > 0)
0410: sb.append(", ");
0411: sb.append("(byte)");
0412: sb.append(ba[i]);
0413: }
0414: } else if (o instanceof char[]) {
0415: char[] ca = (char[]) o;
0416: for (int i = 0; i < ca.length; ++i) {
0417: if (i > 0)
0418: sb.append(", ");
0419: sb.append("'");
0420: sb.append(ca[i]);
0421: sb.append("'");
0422: }
0423: } else if (o instanceof short[]) {
0424: short[] sa = (short[]) o;
0425: for (int i = 0; i < sa.length; ++i) {
0426: if (i > 0)
0427: sb.append(", ");
0428: sb.append("(short)");
0429: sb.append(sa[i]);
0430: }
0431: } else if (o instanceof int[]) {
0432: int[] ia = (int[]) o;
0433: for (int i = 0; i < ia.length; ++i) {
0434: if (i > 0)
0435: sb.append(", ");
0436: sb.append(ia[i]);
0437: }
0438: } else if (o instanceof long[]) {
0439: long[] la = (long[]) o;
0440: for (int i = 0; i < la.length; ++i) {
0441: if (i > 0)
0442: sb.append(", ");
0443: sb.append(la[i]);
0444: sb.append("L");
0445: }
0446: } else if (o instanceof float[]) {
0447: float[] fa = (float[]) o;
0448: for (int i = 0; i < fa.length; ++i) {
0449: if (i > 0)
0450: sb.append(", ");
0451: sb.append(fa[i]);
0452: sb.append("F");
0453: }
0454: } else if (o instanceof double[]) {
0455: double[] da = (double[]) o;
0456: for (int i = 0; i < da.length; ++i) {
0457: if (i > 0)
0458: sb.append(", ");
0459: sb.append(da[i]);
0460: sb.append("D");
0461: }
0462: } else if (o instanceof String) {
0463: // Special-case Strings so we can surround them with quotes.
0464: String[] sa = (String[]) o;
0465: for (int i = 0; i < sa.length; ++i) {
0466: if (i > 0)
0467: sb.append(", ");
0468: sb.append("\"");
0469: sb.append(sa[i]);
0470: sb.append("\"");
0471: }
0472: } else if (cl.startsWith("[L")) {
0473: // Some random class.
0474: Object[] oa = (Object[]) o;
0475: for (int i = 0; i < oa.length; ++i) {
0476: if (i > 0)
0477: sb.append(", ");
0478: sb.append(oa[i]);
0479: }
0480: } else if (cl.startsWith("[[")) {
0481: // Nested arrays.
0482: Object[] aa = (Object[]) o;
0483: for (int i = 0; i < aa.length; ++i) {
0484: if (i > 0)
0485: sb.append(", ");
0486: sb.append(arrayToString(aa[i]));
0487: }
0488: } else
0489: sb.append("(unknown array type)");
0490: sb.append(" }");
0491: return sb.toString();
0492: }
0493:
0494: /// Check if an object extends a given class or one of its superclasses.
0495: // An instanceof that works on Class objects at runtime, instead
0496: // of type descriptors at compile time.
0497: public static boolean instanceOf(Object o, Class cl) {
0498: // Null check.
0499: if (o == null || cl == null)
0500: return false;
0501: Class ocl = o.getClass();
0502: // Check if they are the same class.
0503: if (ocl.equals(cl))
0504: return true;
0505: // If the class is not itself an interface, then check its interfaces.
0506: if (!cl.isInterface()) {
0507: Class ifs[] = cl.getInterfaces();
0508: for (int i = 0; i < ifs.length; ++i)
0509: if (instanceOf(o, ifs[i]))
0510: return true;
0511: }
0512: // And check supeclasses.
0513: Class scl = cl.getSuperclass();
0514: if (scl != null)
0515: if (instanceOf(o, scl))
0516: return true;
0517: // Guess not.
0518: return false;
0519: }
0520:
0521: /// Test is a number is even.
0522: public static boolean even(long n) {
0523: return (n & 1) == 0;
0524: }
0525:
0526: /// Test is a number is odd.
0527: public static boolean odd(long n) {
0528: return (n & 1) != 0;
0529: }
0530:
0531: /// Count the number of 1-bits in a byte.
0532: public static int countOnes(byte n) {
0533: return countOnes(n & 0xffL);
0534: }
0535:
0536: /// Count the number of 1-bits in an int.
0537: public static int countOnes(int n) {
0538: return countOnes(n & 0xffffffffL);
0539: }
0540:
0541: /// Count the number of 1-bits in a long.
0542: public static int countOnes(long n) {
0543: // There are faster ways to do this, all the way up to looking
0544: // up bytes in a 256-element table. But this is not too bad.
0545: int count = 0;
0546: while (n != 0) {
0547: if (odd(n))
0548: ++count;
0549: n >>>= 1;
0550: }
0551: return count;
0552: }
0553:
0554: /// A fixed version of java.io.InputStream.read(byte[], int, int). The
0555: // standard version catches and ignores IOExceptions from below.
0556: // This version sends them on to the caller.
0557: public static int read(InputStream in, byte[] b, int off, int len)
0558: throws IOException {
0559: if (len <= 0)
0560: return 0;
0561: int c = in.read();
0562: if (c == -1)
0563: return -1;
0564: if (b != null)
0565: b[off] = (byte) c;
0566: int i;
0567: for (i = 1; i < len; ++i) {
0568: c = in.read();
0569: if (c == -1)
0570: break;
0571: if (b != null)
0572: b[off + i] = (byte) c;
0573: }
0574: return i;
0575: }
0576:
0577: /// A version of read that reads the entire requested block, instead
0578: // of sometimes terminating early.
0579: // @return -1 on EOF, otherwise len
0580: public static int readFully(InputStream in, byte[] b, int off,
0581: int len) throws IOException {
0582: int l, r;
0583: for (l = 0; l < len;) {
0584: r = read(in, b, l, len - l);
0585: if (r == -1)
0586: return -1;
0587: l += r;
0588: }
0589: return len;
0590: }
0591:
0592: /// Make a URL with no ref part and no query string. Also, if it's
0593: // a directory then make sure there's a trailing slash.
0594: public static URL plainUrl(URL context, String urlStr)
0595: throws MalformedURLException {
0596: URL url = new URL(context, urlStr);
0597: String fileStr = url.getFile();
0598: int i = fileStr.indexOf('?');
0599: if (i != -1)
0600: fileStr = fileStr.substring(0, i);
0601: url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
0602: fileStr);
0603: if ((!fileStr.endsWith("/"))
0604: && urlStrIsDir(url.toExternalForm())) {
0605: fileStr = fileStr + "/";
0606: url = new URL(url.getProtocol(), url.getHost(), url
0607: .getPort(), fileStr);
0608: }
0609: return url;
0610: }
0611:
0612: /// Make a URL with no ref part and no query string. Also, if it's
0613: // a directory then make sure there's a trailing slash.
0614: public static URL plainUrl(String urlStr)
0615: throws MalformedURLException {
0616: return plainUrl(null, urlStr);
0617: }
0618:
0619: /// Figure out the base URL for a given URL. What this means is
0620: // if the URL points to a directory, you get that directory; if the
0621: // URL points to a file, you get the directory the file is in.
0622: public static String baseUrlStr(String urlStr) {
0623: if (urlStr.endsWith("/"))
0624: return urlStr;
0625: if (urlStrIsDir(urlStr))
0626: return urlStr + "/";
0627: return urlStr.substring(0, urlStr.lastIndexOf('/') + 1);
0628: }
0629:
0630: /// Makes sure if a URL is a directory, it ends with a slash.
0631: public static String fixDirUrlStr(String urlStr) {
0632: if (urlStr.endsWith("/"))
0633: return urlStr;
0634: if (urlStrIsDir(urlStr))
0635: return urlStr + "/";
0636: return urlStr;
0637: }
0638:
0639: /// Figures out whether a URL points to a directory or not.
0640: // Web servers are lenient and accept directory-URLs without
0641: // the trailing slash. What they actually do is return a
0642: // redirect to the same URL with the trailing slash appended.
0643: // Unfortunately, Java doesn't let us see that such a redirect
0644: // happened. Instead we have to figure out it's a directory
0645: // indirectly and heuristically.
0646: public static boolean urlStrIsDir(String urlStr) {
0647: // If it ends with a slash, it's probably a directory.
0648: if (urlStr.endsWith("/"))
0649: return true;
0650:
0651: // If the last component has a dot, it's probably not a directory.
0652: int lastSlash = urlStr.lastIndexOf('/');
0653: int lastPeriod = urlStr.lastIndexOf('.');
0654: if (lastPeriod != -1
0655: && (lastSlash == -1 || lastPeriod > lastSlash))
0656: return false;
0657:
0658: // Otherwise, append a slash and try to connect. This is
0659: // fairly expensive.
0660: String urlStrWithSlash = urlStr + "/";
0661: try {
0662: URL url = new URL(urlStrWithSlash);
0663: InputStream f = url.openStream();
0664: f.close();
0665: // Worked fine - it's probably a directory.
0666: return true;
0667: } catch (Exception e) {
0668: // Got an error - must not be a directory.
0669: return false;
0670: }
0671: }
0672:
0673: // Figures out whether a URL is absolute or not.
0674: public static boolean urlStrIsAbsolute(String urlStr) {
0675: if (urlStr.startsWith("/") || urlStr.indexOf(":/") != -1)
0676: return true;
0677: // Should handle :8000/ and such too.
0678: return false;
0679: }
0680:
0681: // Returns an equivalent URL string that is guaranteed to be absolute.
0682: public static String absoluteUrlStr(String urlStr, URL contextUrl)
0683: throws MalformedURLException {
0684: URL url = new URL(contextUrl, urlStr);
0685: return url.toExternalForm();
0686: }
0687:
0688: /// URLDecoder to go along with java.net.URLEncoder. Why there isn't
0689: // already a decoder in the standard library is a mystery to me.
0690: public static String urlDecoder(String encoded) {
0691: StringBuffer decoded = new StringBuffer();
0692: int len = encoded.length();
0693: for (int i = 0; i < len; ++i) {
0694: if (encoded.charAt(i) == '%' && i + 2 < len) {
0695: int d1 = Character.digit(encoded.charAt(i + 1), 16);
0696: int d2 = Character.digit(encoded.charAt(i + 2), 16);
0697: if (d1 != -1 && d2 != -1)
0698: decoded.append((char) ((d1 << 4) + d2));
0699: i += 2;
0700: } else if (encoded.charAt(i) == '+')
0701: decoded.append(' ');
0702: else
0703: decoded.append(encoded.charAt(i));
0704: }
0705: return decoded.toString();
0706: }
0707:
0708: /// A base-64 encoder, necessary for doing the client side of Basic
0709: // Authentication. This encodes binary data as printable ASCII
0710: // characters. Three 8-bit binary bytes are turned into four 6-bit
0711: // values, like so:
0712: //
0713: // [11111111] [22222222] [33333333]
0714: //
0715: // [111111] [112222] [222233] [333333]
0716: //
0717: // Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
0718: public static String base64Encode(byte[] src) {
0719: StringBuffer encoded = new StringBuffer();
0720: int i, phase = 0;
0721: char c = 0;
0722:
0723: for (i = 0; i < src.length; ++i) {
0724: switch (phase) {
0725: case 0:
0726: c = b64EncodeTable[(src[i] >> 2) & 0x3f];
0727: encoded.append(c);
0728: c = b64EncodeTable[(src[i] & 0x3) << 4];
0729: encoded.append(c);
0730: ++phase;
0731: break;
0732: case 1:
0733: c = b64EncodeTable[(b64DecodeTable[c] | (src[i] >> 4)) & 0x3f];
0734: encoded.setCharAt(encoded.length() - 1, c);
0735: c = b64EncodeTable[(src[i] & 0xf) << 2];
0736: encoded.append(c);
0737: ++phase;
0738: break;
0739: case 2:
0740: c = b64EncodeTable[(b64DecodeTable[c] | (src[i] >> 6)) & 0x3f];
0741: encoded.setCharAt(encoded.length() - 1, c);
0742: c = b64EncodeTable[src[i] & 0x3f];
0743: encoded.append(c);
0744: phase = 0;
0745: break;
0746: }
0747: }
0748: /* Pad with ='s. */
0749: while (phase++ < 3)
0750: encoded.append('=');
0751: return encoded.toString();
0752: }
0753:
0754: private static char b64EncodeTable[] = { 'A', 'B', 'C', 'D', 'E',
0755: 'F', 'G', 'H', // 00-07
0756: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 08-15
0757: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16-23
0758: 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24-31
0759: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32-39
0760: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40-47
0761: 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48-55
0762: '4', '5', '6', '7', '8', '9', '+', '/' // 56-63
0763: };
0764:
0765: private static int b64DecodeTable[] = { -1,
0766: -1,
0767: -1,
0768: -1,
0769: -1,
0770: -1,
0771: -1,
0772: -1,
0773: -1,
0774: -1,
0775: -1,
0776: -1,
0777: -1,
0778: -1,
0779: -1,
0780: -1, // 00-0F
0781: -1, -1,
0782: -1,
0783: -1,
0784: -1,
0785: -1,
0786: -1,
0787: -1,
0788: -1,
0789: -1,
0790: -1,
0791: -1,
0792: -1,
0793: -1,
0794: -1,
0795: -1, // 10-1F
0796: -1, -1, -1,
0797: -1,
0798: -1,
0799: -1,
0800: -1,
0801: -1,
0802: -1,
0803: -1,
0804: -1,
0805: 62,
0806: -1,
0807: -1,
0808: -1,
0809: 63, // 20-2F
0810: 52, 53, 54, 55,
0811: 56,
0812: 57,
0813: 58,
0814: 59,
0815: 60,
0816: 61,
0817: -1,
0818: -1,
0819: -1,
0820: -1,
0821: -1,
0822: -1, // 30-3F
0823: -1, 0, 1, 2, 3,
0824: 4,
0825: 5,
0826: 6,
0827: 7,
0828: 8,
0829: 9,
0830: 10,
0831: 11,
0832: 12,
0833: 13,
0834: 14, // 40-4F
0835: 15, 16, 17, 18, 19,
0836: 20,
0837: 21,
0838: 22,
0839: 23,
0840: 24,
0841: 25,
0842: -1,
0843: -1,
0844: -1,
0845: -1,
0846: -1, // 50-5F
0847: -1, 26, 27, 28, 29, 30,
0848: 31,
0849: 32,
0850: 33,
0851: 34,
0852: 35,
0853: 36,
0854: 37,
0855: 38,
0856: 39,
0857: 40, // 60-6F
0858: 41, 42, 43, 44, 45, 46, 47,
0859: 48,
0860: 49,
0861: 50,
0862: 51,
0863: -1,
0864: -1,
0865: -1,
0866: -1,
0867: -1, // 70-7F
0868: -1, -1, -1, -1, -1, -1, -1, -1,
0869: -1,
0870: -1,
0871: -1,
0872: -1,
0873: -1,
0874: -1,
0875: -1,
0876: -1, // 80-8F
0877: -1, -1, -1, -1, -1, -1, -1, -1, -1,
0878: -1,
0879: -1,
0880: -1,
0881: -1,
0882: -1,
0883: -1,
0884: -1, // 90-9F
0885: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0886: -1,
0887: -1,
0888: -1,
0889: -1,
0890: -1,
0891: -1, // A0-AF
0892: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0893: -1,
0894: -1,
0895: -1,
0896: -1,
0897: -1, // B0-BF
0898: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0899: -1,
0900: -1,
0901: -1,
0902: -1, // C0-CF
0903: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0904: -1,
0905: -1,
0906: -1, // D0-DF
0907: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0908: -1,
0909: -1, // E0-EF
0910: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0911: -1 // F0-FF
0912: };
0913:
0914: /// A base-64 encoder that takes a String, for convenience.
0915: public static String base64Encode(String srcString) {
0916: byte[] src = new byte[srcString.length()];
0917: srcString.getBytes(0, src.length, src, 0);
0918: return base64Encode(src);
0919: }
0920:
0921: /// Check if an array contains a given element.
0922: public static boolean arraycontains(Object[] array, Object element) {
0923: for (int i = 0; i < array.length; ++i)
0924: if (array[i].equals(element))
0925: return true;
0926: return false;
0927: }
0928:
0929: /// Run a program on the host system.
0930: // <P>
0931: // This routine runs the specified command, waits for it to
0932: // finish, and returns the exit status.
0933: // This is like the Unix system() routine. Unlike the Unix version,
0934: // though, stdout and stderr get thrown away unless you redirect them.
0935: public static int system(String cmd) {
0936: try {
0937: return runCommand(cmd).waitFor();
0938: } catch (IOException e) {
0939: return -1;
0940: } catch (InterruptedException e) {
0941: return -1;
0942: }
0943: }
0944:
0945: /// Run a program on the host system, and capture the output.
0946: // <P>
0947: // This routine runs the specified command, and returns an InputStream
0948: // for reading the output of the program.
0949: // <P>
0950: // <B>WARNING:</B> In JDK1.0.2 there is a serious bug in the process
0951: // IO routines, such that reading all the way to the end of a process's
0952: // output will invariably get you an IOException( "read error" ).
0953: // In some cases you will also <B>lose</B> the last bufferload of
0954: // the output. The workaround is to add a " ; sleep 1" to the end of
0955: // your command, and to ignore the "read error" IOException.
0956: public static InputStream popenr(String cmd) {
0957: try {
0958: return runCommand(cmd).getInputStream();
0959: } catch (IOException e) {
0960: return null;
0961: }
0962: }
0963:
0964: /// Run a program on the host system, and send it some input.
0965: // <P>
0966: // This routine runs the specified command, and returns an OutputStream
0967: // for writing the program's input.
0968: public static OutputStream popenw(String cmd) {
0969: try {
0970: return runCommand(cmd).getOutputStream();
0971: } catch (IOException e) {
0972: return null;
0973: }
0974: }
0975:
0976: /// Run a program on the host system.
0977: // <P>
0978: // This routine runs the specified command, and returns a Process
0979: // object so you can do what you like with it.
0980: // <P>
0981: // <B>WARNING:</B> In JDK1.0.2 there is a serious bug in the process
0982: // IO routines, such that reading all the way to the end of a process's
0983: // output will invariably get you an IOException( "read error" ).
0984: // In some cases you will also <B>lose</B> the last bufferload of
0985: // the output. The workaround is to add a " ; sleep 1" to the end of
0986: // your command, and to ignore the "read error" IOException.
0987: public static Process runCommand(String cmd) throws IOException {
0988: Runtime runtime = Runtime.getRuntime();
0989: String[] shCmd = new String[3];
0990: shCmd[0] = "/bin/sh";
0991: shCmd[1] = "-c";
0992: shCmd[2] = cmd;
0993: return runtime.exec(shCmd);
0994: }
0995:
0996: /// Copy the input to the output until EOF.
0997: public static void copyStream(InputStream in, OutputStream out)
0998: throws IOException {
0999: byte[] buf = new byte[4096];
1000: int len;
1001: while ((len = in.read(buf)) != -1)
1002: out.write(buf, 0, len);
1003: }
1004:
1005: /// Copy the input to the output until EOF.
1006: public static void copyStream(Reader in, Writer out)
1007: throws IOException {
1008: char[] buf = new char[4096];
1009: int len;
1010: while ((len = in.read(buf)) != -1)
1011: out.write(buf, 0, len);
1012: }
1013:
1014: /// Copy the input to the output until EOF.
1015: public static void copyStream(InputStream in, Writer out)
1016: throws IOException {
1017: byte[] buf1 = new byte[4096];
1018: char[] buf2 = new char[4096];
1019: int len, i;
1020: while ((len = in.read(buf1)) != -1) {
1021: for (i = 0; i < len; ++i)
1022: buf2[i] = (char) buf1[i];
1023: out.write(buf2, 0, len);
1024: }
1025: }
1026:
1027: /// Copy the input to the output until EOF.
1028: public static void copyStream(Reader in, OutputStream out)
1029: throws IOException {
1030: char[] buf1 = new char[4096];
1031: byte[] buf2 = new byte[4096];
1032: int len, i;
1033: while ((len = in.read(buf1)) != -1) {
1034: for (i = 0; i < len; ++i)
1035: buf2[i] = (byte) buf1[i];
1036: out.write(buf2, 0, len);
1037: }
1038: }
1039:
1040: /// Dump out the current call stack.
1041: public static void dumpStack(PrintStream p) {
1042: (new Throwable()).printStackTrace(p);
1043: }
1044:
1045: /// Dump out the current call stack onto System.err.
1046: public static void dumpStack() {
1047: (new Throwable()).printStackTrace();
1048: }
1049:
1050: }
|