// Utils - assorted static utility routines
//
// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
//
// Base64 code borrowed from public domain supported by Robert Harder
// Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
// periodically to check for updates or to contribute improvements.
//
// All enhancements Copyright (C)1998-2010 by Dmitriy Rogatkin
//
// $Id: Utils.java,v 1.37 2011/06/10 05:08:04 dmitriy Exp $
package Acme;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
/// Assorted static utility routines.
// <P>
// Whenever I come up with a static routine that might be of general use,
// I put it here. So far the class includes:
// <UL>
// <LI> some string routines that were left out of java.lang.String
// <LI> a general array-to-string routine
// <LI> a fixed version of java.io.InputStream's byte-array read routine
// <LI> a bunch of URL-hacking routines
// <LI> some easy-to-use wrappers for Runtime.exec
// <LI> a debugging routine to dump the current call stack
// <LI> a URLDecoder to match java.net.URLEncoder
// </UL>
// and lots more.
// <P>
// <A HREF="/resources/classes/Acme/Utils.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
public class Utils {
// / Returns a date string formatted in Unix ls style - if it's within
// six months of now, Mmm dd hh:ss, else Mmm dd yyyy.
static final SimpleDateFormat shortfmt = new SimpleDateFormat("MMM dd HH:mm");
static final SimpleDateFormat longfmt = new SimpleDateFormat("MMM dd yyyy");
public static final int COPY_BUF_SIZE = 4096 * 2;
public final static String ISO_8859_1 = "ISO-8859-1";
public static final Class[] EMPTY_CLASSES = {};
public static final Object[] EMPTY_OBJECTS = {};
public static final Enumeration EMPTY_ENUMERATION = new Enumeration() {
public boolean hasMoreElements() {
return false;
}
public Object nextElement() {
return null;
}
};
public static String lsDateStr(Date date) {
if (Math.abs(System.currentTimeMillis() - date.getTime()) < 183L * 24L * 60L * 60L * 1000L)
return shortfmt.format(date);
else
return longfmt.format(date);
}
public static Hashtable parseQueryString(String query, String encoding) {
Hashtable result = new Hashtable();
if (encoding == null)
encoding = "UTF-8";
StringTokenizer st = new StringTokenizer(query, "&");
while (st.hasMoreTokens()) {
String pair = st.nextToken();
int ep = pair.indexOf('=');
String key = ep > 0 ? pair.substring(0, ep) : pair;
String value = ep > 0 ? pair.substring(ep + 1) : "";
try {
key = /* URLDecoder. */decode(key, encoding);
if (value != null)
value = /* URLDecoder. */decode(value, encoding);
} catch (UnsupportedEncodingException uee) {
}
String[] values = (String[]) result.get(key);
String[] newValues;
if (values == null) {
newValues = new String[1];
newValues[0] = value;
} else {
newValues = new String[values.length + 1];
System.arraycopy(values, 0, newValues, 0, values.length);
newValues[values.length] = value;
}
result.put(key, newValues);
}
return result;
}
public static Map parsePostData(long len, InputStream is, String encoding, String[] cachedStream)
throws IOException {
// TODO: handle parsing data over 2 GB
if (len > Integer.MAX_VALUE)
throw new RuntimeException("Can't process POST data over " + Integer.MAX_VALUE + ", requested: " + len);
byte[] buf = new byte[(int) len];
int fp = 0;
while (fp < len) {
int c = is.read(buf, fp, buf.length - fp);
if (c < 0)
break;
fp += c;
}
//System.err.println("====>"+new String( buf));
if (cachedStream != null && cachedStream.length > 0)
return parseQueryString(cachedStream[0] = new String(buf, 0, fp, ISO_8859_1), encoding);
else
return parseQueryString(new String(buf, 0, fp, ISO_8859_1), encoding);
}
/**
* Decodes URL encoded string including newly introduced JavaScript encoding with %uxxxx chars
*
* @param s
* encoded string
* @param enc
* source encoding
* @return decoded string or original if no decoding required
* @throws UnsupportedEncodingException
*/
public static String decode(String s, String enc) throws UnsupportedEncodingException {
if (enc == null || enc.length() == 0) {
throw new UnsupportedEncodingException("decode: no source char encoding provided.");
}
if (s == null)
return null;
boolean decoded = false;
int l = s.length();
StringBuffer sb = new StringBuffer(l > 1024 ? l / 3 : l);
int state = sText;
int i = 0;
int code = 0;
char c;
int pos = 0;
int ofs = 0;
byte[] buf = null;
boolean processDig = false;
while (i < l) {
c = s.charAt(i);
switch (c) {
case '+':
decoded = true;
if (state == sText)
sb.append(' ');
else if (state == s2Dig) {
sb.append(new String(buf, 0, pos + 1, enc));
state = sText;
sb.append(' ');
} else
new IllegalArgumentException("decode: unexpected + at pos: " + i + ", of : " + s);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
ofs = '0';
processDig = true;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
ofs = 'a' - 10;
processDig = true;
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
ofs = 'A' - 10;
processDig = true;
break;
case '%':
decoded = true;
if (state == sText) {
state = sEscape;
if (buf == null)
buf = new byte[(l - i) / 3];
pos = 0;
} else if (state == s2Dig) {
state = sEscape;
pos++;
} else
new IllegalArgumentException("decode: unexpected escape % at pos: " + i + ", of : " + s);
break;
case 'u':
if (state == sEscape) {
if (pos > 0) {
sb.append(new String(buf, 0, pos, enc));
pos = 0;
}
state = sU1;
} else if (state == sText) {
sb.append(c);
} else if (state == s2Dig) {
sb.append(new String(buf, 0, pos + 1, enc));
state = sText;
sb.append(c);
} else
new IllegalArgumentException("decode: unexpected char in hex at pos: " + i + ", of : " + s);
break;
default:
if (state == sText)
sb.append(c);
else if (state == s2Dig) {
sb.append(new String(buf, 0, pos + 1, enc));
state = sText;
sb.append(c);
} else
new IllegalArgumentException("decode: unexpected char in hex at pos: " + i + ", of : " + s);
break;
}
i++;
if (processDig) {
if (state == sEscape) {
code = c - ofs;
state = s1Dig;
} else if (state == s1Dig) {
buf[pos] = (byte) (code * 16 + (c - ofs));
state = s2Dig;
} else if (state == s2Dig) { // escape finished
sb.append(new String(buf, 0, pos + 1, enc));
state = sText;
sb.append(c);
} else if (state == sU1) {
code = c - ofs;
state = sU2;
} else if (state == sU2) {
code = code * 16 + c - ofs;
state = sU3;
} else if (state == sU3) {
code = code * 16 + c - ofs;
state = sU4;
} else if (state == sU4) {
sb.append((char) (code * 16 + c - ofs));
state = sText;
} else
sb.append(c);
processDig = false;
}
}
if (state == s2Dig)
sb.append(new String(buf, 0, pos + 1, enc));
return (decoded ? sb.toString() : s);
}
private static final int sText = 0;
private static final int s1Dig = 1;
private static final int s2Dig = 2;
private static final int sEscape = 3;
private static final int sU1 = 4;
private static final int sU2 = 5;
private static final int sU3 = 6;
private static final int sU4 = 7;
public static String htmlEncode(String s, boolean encodeWS) {
if (s == null)
return null;
char[] ca = s.toCharArray();
StringBuffer res = new StringBuffer(ca.length);
int ls = 0;
boolean blankMet = true;
for (int i = 0; i < ca.length; i++) {
switch (ca[i]) {
case '<':
res.append(ca, ls, i - ls);
res.append("<");
ls = i + 1;
break;
case '>':
res.append(ca, ls, i - ls);
res.append(">");
ls = i + 1;
break;
case '"':
res.append(ca, ls, i - ls);
res.append(""");
ls = i + 1;
break;
case '&':
res.append(ca, ls, i - ls);
res.append("&");
ls = i + 1;
break;
case ' ':
if (blankMet && encodeWS) {
res.append(ca, ls, i - ls);
res.append(" ");
ls = i + 1;
} else
blankMet = true;
break;
case '\n':
if (encodeWS) {
res.append(ca, ls, i - ls);
res.append("<BR>");
ls = i + 1;
}
break;
case '\r':
if (encodeWS) {
res.append(ca, ls, i - ls);
ls = i + 1;
}
break;
default:
if (ca[i] > 127) { // no unicode
res.append(ca, ls, i - ls);
res.append("&#;" + (int) ca[i]);
ls = i + 1;
}
blankMet = false;
}
}
if (ls < ca.length)
res.append(ca, ls, ca.length - ls);
return res.toString();
}
public static float isGzipAccepted(String contentEncoding) {
float result = 0f;
if (contentEncoding != null) {
int gzsl = "gzip;".length();
int zp = contentEncoding.indexOf("gzip");
if (zp >= 0) {
if (contentEncoding.length() > (zp+gzsl) &&contentEncoding.charAt(zp + gzsl) == ';') {
zp = contentEncoding.indexOf("q=", zp + gzsl);
if (zp > 0) {
int qe = contentEncoding.indexOf(",", zp);
if (qe < 0)
qe = contentEncoding.length();
try {
result = Float.parseFloat(contentEncoding.substring(zp + 2, qe));
} catch (NumberFormatException e) {
}
}
} else
result = 1f;
}
}
return result;
}
// / Checks whether a string matches a given wildcard pattern.
// Only does ? and *, and multiple patterns separated by |.
public static boolean match(String pattern, String string) {
for (int p = 0;; ++p) {
for (int s = 0;; ++p, ++s) {
boolean sEnd = (s >= string.length());
boolean pEnd = (p >= pattern.length() || pattern.charAt(p) == '|');
if (sEnd && pEnd)
return true;
if (sEnd || pEnd)
break;
if (pattern.charAt(p) == '?')
continue;
if (pattern.charAt(p) == '*') {
int i;
++p;
for (i = string.length(); i >= s; --i)
if (match(pattern.substring(p), string.substring(i))) // not quite right
return true;
break;
}
if (pattern.charAt(p) != string.charAt(s))
break;
}
p = pattern.indexOf('|', p);
if (p == -1)
return false;
}
}
// / Finds the maximum length of a string that matches a given wildcard
// pattern. Only does ? and *, and multiple patterns separated by |.
public static int matchSpan(String pattern, String string) {
int result = 0;
StringTokenizer st = new StringTokenizer(pattern, "|");
while (st.hasMoreTokens()) {
int len = matchSpan1(st.nextToken(), string);
if (len > result)
result = len;
}
return result;
}
static int matchSpan1(String pattern, String string) {
int p = 0;
for (; p < string.length() && p < pattern.length(); p++) {
if (pattern.charAt(p) == string.charAt(p))
continue;
if (pattern.charAt(p) == '*')
return p - 1;
return 0;
}
return p < (pattern.length() - 1) ? -1 : p;
}
// / Turns a String into an array of Strings, by using StringTokenizer
// to split it up at whitespace.
public static String[] splitStr(String str) {
StringTokenizer st = new StringTokenizer(str);
int n = st.countTokens();
String[] strs = new String[n];
for (int i = 0; i < n; ++i)
strs[i] = st.nextToken();
return strs;
}
// / Turns a String into an array of Strings, by splitting it at
// the specified character. This does not use StringTokenizer,
// and therefore can handle empty fields.
public static String[] splitStr(String str, char delim) {
int n = 1;
int index = -1;
while (true) {
index = str.indexOf(delim, index + 1);
if (index == -1)
break;
++n;
}
String[] strs = new String[n];
index = -1;
for (int i = 0; i < n - 1; ++i) {
int nextIndex = str.indexOf(delim, index + 1);
strs[i] = str.substring(index + 1, nextIndex);
index = nextIndex;
}
strs[n - 1] = str.substring(index + 1);
return strs;
}
public static String[] splitStr(String str, String quotes) {
char[] ca = str.toCharArray();
// List result = new ArrayList(10);
String[] result = new String[0];
boolean inArg = false;
boolean quoted = false;
int argStart = -1;
for (int i = 0; i < ca.length; i++) {
char c = ca[i];
if (inArg) {
if (quoted) {
if (quotes.indexOf(c) >= 0) {
result = copyOf(result, result.length + 1);
result[result.length - 1] = new String(ca, argStart, i - argStart);
argStart = -1;
quoted = false;
inArg = false;
}
} else {
if (c == ' ') {
result = copyOf(result, result.length + 1);
result[result.length - 1] = new String(ca, argStart, i - argStart);
argStart = -1;
inArg = false;
}
}
} else {
if (c != ' ') {
inArg = true;
if (quotes.indexOf(c) >= 0) {
quoted = true;
argStart = i + 1;
} else
argStart = i;
}
}
}
if (argStart > 0) {
result = copyOf(result, result.length + 1);
result[result.length - 1] = new String(ca, argStart, ca.length - argStart);
}
// for(int i=0;i<result.length;i++)
// System.err.println("Param["+i+"]="+result[i]);
return result;
}
public static String[] copyOf(String[] original, int newLength) {
return copyOfRange(original, 0, newLength);
}
public static String[] copyOfRange(String[] original, int from, int newLength) {
String[] copy = new String[newLength];
newLength = Math.min(original.length-from, newLength);
System.arraycopy(original, from, copy, 0, newLength);
return copy;
}
/*
public static Object[] copyOf(Object[] original, int from, int newLength) {
newLength = Math.min(original.length - from, newLength);
Object[] copy = new Object[newLength];
System.arraycopy(original, from, copy, 0, newLength);
return copy;
}
*/
public static String canonicalizePath(String path) {
if (path == null || path.length() == 0)
return path;
List pathElems = new ArrayList(6);
char[] pa = path.toCharArray();
int n = pa.length;
int s = -1;
int lev = 0;
for (int i = 0; i < n; i++) {
if (s < 0) {
if (pa[i] != '/' && pa[i] != '\\')
s = i;
} else {
boolean f = false;
if (pa[i] == '?')
f = true;
if (pa[i] == '/' || pa[i] == '\\' || f) {
String el = new String(pa, s, i - s);
if (el.equals("..")) {
if (pathElems.size() > 0)
pathElems.remove(pathElems.size() - 1);
else
lev--;
// else exception ?
} else if (el.equals(".") == false)
if (lev >= 0)
pathElems.add(el);
else
lev++;
if (f) {
s = i;
break;
}
s = -1;
}
}
}
if (s > 0) {
String el = new String(pa, s, n - s);
if (el.equals("..")) {
if (pathElems.size() > 0)
pathElems.remove(pathElems.size() - 1);
// else exception ?
} else if (el.equals(".") == false)
if (lev >= 0)
pathElems.add(el);
} else
pathElems.add("");
if (pathElems.size() == 0)
return lev>=0?"":null;
StringBuffer result = new StringBuffer(n);
result.append(pathElems.get(0));
n = pathElems.size();
for (int i = 1; i < n; i++)
result.append('/').append(pathElems.get(i));
// System.err.println("Before "+path+" after "+result);
return result.toString();
}
// / Copy the input to the output until EOF.
public static void copyStream(InputStream in, OutputStream out, long maxLen) throws IOException {
byte[] buf = new byte[COPY_BUF_SIZE];
int len;
if (maxLen <= 0)
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len);
else
while ((len = in.read(buf)) > 0)
if (len <= maxLen) {
out.write(buf, 0, len);
maxLen -= len;
} else {
out.write(buf, 0, (int) maxLen);
break;
}
}
// / Copy the input to the output until EOF.
public static void copyStream(Reader in, Writer out) throws IOException {
char[] buf = new char[COPY_BUF_SIZE];
int len;
while ((len = in.read(buf)) != -1)
out.write(buf, 0, len);
}
// / Copy the input to the output until EOF.
public static void copyStream(Reader in, OutputStream out, String charSet) throws IOException {
char[] buf = new char[4096];
int len;
if (charSet == null)
while ((len = in.read(buf)) != -1) {
out.write(new String(buf, 0, len).getBytes());
}
else
while ((len = in.read(buf)) != -1)
out.write(new String(buf, 0, len).getBytes(charSet));
}
protected final static char BASE64ARRAY[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/' };
/**
* base 64 encoding, string converted to bytes using specified encoding
*
* @param String
* <val>_s</val> original string to encode
* @param String
* encoding, can be null, then iso-8859-1 used
* @return String result of encoding as iso-8859-1 string<br>
* return null in case of invalid encoding or original string null
* @exception no
* exceptions
*/
public final static String base64Encode(String _s, String _enc) {
if (_s == null)
return null;
if (_enc == null)
_enc = ISO_8859_1;
try {
return base64Encode(_s.getBytes(_enc));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* base 64 encoding, array of bytes converted to bytes using specified encoding
*
* @param String
* <val>_s</val> original string to encode
* @param String
* encoding, can be null, then iso-8859-1 used
* @return String result of encoding as iso-8859-1 string<br>
*
* @exception <code>NullPointerException</code> if input parameter is null
*/
public final static String base64Encode(byte[] _bytes) {
StringBuffer encodedBuffer = new StringBuffer((int) (_bytes.length * 1.5));
int i = 0;
int pad = 0;
while (i < _bytes.length) {
int b1 = (0xFF & _bytes[i++]);
int b2;
int b3;
if (i >= _bytes.length) {
b2 = 0;
b3 = 0;
pad = 2;
} else {
b2 = 0xFF & _bytes[i++];
if (i >= _bytes.length) {
b3 = 0;
pad = 1;
} else
b3 = (0xFF & _bytes[i++]);
}
byte c1 = (byte) (b1 >> 2);
byte c2 = (byte) (((b1 & 0x3) << 4) | (b2 >> 4));
byte c3 = (byte) (((b2 & 0xf) << 2) | (b3 >> 6));
byte c4 = (byte) (b3 & 0x3f);
encodedBuffer.append(BASE64ARRAY[c1]).append(BASE64ARRAY[c2]);
switch (pad) {
case 0:
encodedBuffer.append(BASE64ARRAY[c3]).append(BASE64ARRAY[c4]);
break;
case 1:
encodedBuffer.append(BASE64ARRAY[c3]).append('=');
break;
case 2:
encodedBuffer.append("==");
break;
}
}
return encodedBuffer.toString();
}
/**
* Translates a Base64 value to either its 6-bit reconstruction value or a negative number indicating some other meaning.
*/
protected final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
// 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9, -9, -9, // Decimal 44 - 46
63, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
// through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
// through 'Z'
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
// through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
// through 'z'
-9, -9, -9, -9 // Decimal 123 - 126
};
protected final static byte WHITE_SPACE_ENC = -5; // Indicates white space
// in encoding
protected final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign
// in encoding
/** The equals sign (=) as a byte. */
protected final static byte EQUALS_SIGN = (byte) '=';
/**
* base 64 decoding
*
* @param encoded
* string
* @param encoding
* used to get string bytes
* @return result of encoding, or null if encoding invalid or string null, or string is invalid base 64 encoding
*/
public final static String base64Decode(String _s, String _enc) {
if (_s == null)
return null;
if (_enc == null)
_enc = ISO_8859_1;
try {
return new String(decode64(_s), _enc);
} catch (UnsupportedEncodingException uee) {
}
return null;
}
/**
* Decodes four bytes from array <var>source</var> and writes the resulting bytes (up to three of them) to <var>destination</var>. The source and
* destination arrays can be manipulated anywhere along their length by specifying <var>srcOffset</var> and <var>destOffset</var>. This method does not
* check to make sure your arrays are large enough to accomodate <var>srcOffset</var> + 4 for the <var>source</var> array or <var>destOffset</var> + 3
* for the <var>destination</var> array. This method returns the actual number of bytes that were converted from the Base64 encoding.
*
*
* @param source
* the array to convert
* @param srcOffset
* the index where conversion begins
* @param destination
* the array to hold the conversion
* @param destOffset
* the index where output will be put
* @return the number of decoded bytes converted
* @since 1.3
*/
private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
// Two ways to do the same thing. Don't know which way I like best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
// )
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
}
// Example: DkL=
else if (source[srcOffset + 3] == EQUALS_SIGN) {
// Two ways to do the same thing. Don't know which way I like best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
// )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
}
// Example: DkLE
else {
try {
// Two ways to do the same thing. Don't know which way I like
// best.
// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
// >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
| ((DECODABET[source[srcOffset + 3]] & 0xFF));
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
} catch (Exception e) {
System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
return -1;
} // e nd catch
}
} // end decodeToBytes
/**
* Very low-level access to decoding ASCII characters in the form of a byte array. Does not support automatically gunzipping or any other "fancy" features.
*
* @param source
* The Base64 encoded data
* @param off
* The offset of where to begin decoding
* @param len
* The length of characters to decode
* @return decoded data
* @since 1.3
*/
public static byte[] decode(byte[] source, int off, int len) {
int len34 = len * 3 / 4;
byte[] outBuff = new byte[len34]; // Upper limit on size of output
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiCrop = 0;
byte sbiDecode = 0;
for (i = off; i < off + len; i++) {
sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
sbiDecode = DECODABET[sbiCrop];
if (sbiDecode >= WHITE_SPACE_ENC) // Whitesp ace,Eq ualssi gnor be
// tter
{
if (sbiDecode >= EQUALS_SIGN_ENC) {
b4[b4Posn++] = sbiCrop;
if (b4Posn > 3) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
b4Posn = 0;
// If that was the equals sign, break out of 'for' loop
if (sbiCrop == EQUALS_SIGN)
break;
} // end if: quartet built
} // end if: equals sign or better
} // end if: white space, equals sign or better
else {
System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
return null;
} // end else:
} // each input character
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
} // end decode
/**
* Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
*
* @param s
* the string to decode
* @return the decoded data
* @since 1.4
*/
public static byte[] decode64(String s) {
byte[] bytes;
try {
bytes = s.getBytes(ISO_8859_1);
} // end try
catch (java.io.UnsupportedEncodingException uee) {
bytes = s.getBytes();
} // end catch
// </change>
// Decode
bytes = decode(bytes, 0, bytes.length);
// Check to see if it's gzip-compressed
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
if (bytes != null && bytes.length >= 4) {
int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
java.io.ByteArrayInputStream bais = null;
java.util.zip.GZIPInputStream gzis = null;
java.io.ByteArrayOutputStream baos = null;
byte[] buffer = new byte[2048];
int length = 0;
try {
baos = new java.io.ByteArrayOutputStream();
bais = new java.io.ByteArrayInputStream(bytes);
gzis = new java.util.zip.GZIPInputStream(bais);
while ((length = gzis.read(buffer)) >= 0) {
baos.write(buffer, 0, length);
} // end while: reading input
// No error? Get new bytes.
bytes = baos.toByteArray();
} // end try
catch (java.io.IOException e) {
// Just return originally-decoded bytes
} // end catch
finally {
try {
baos.close();
} catch (Exception e) {
}
try {
gzis.close();
} catch (Exception e) {
}
try {
bais.close();
} catch (Exception e) {
}
} // end finally
} // end if: gzipped
} // end if: bytes.length >= 2
return bytes;
} // end decode
/**
* calculate local file based class path for class loader if possible (servlet classes must be located there)
*
* @param cl
* class loader
* @return class path in string
*/
static public String calculateClassPath(ClassLoader cl) {
// scan cl chain to find
StringBuffer classPath = new StringBuffer();
boolean jspFound = false, servletFound = false;
while (cl != null) {
if (cl instanceof URLClassLoader) {
boolean addClasses = false;
if (jspFound == false) {
jspFound = ((URLClassLoader) cl).findResource("javax/servlet/jsp/JspPage.class") != null;
addClasses |= jspFound;
}
if (servletFound == false) {
servletFound = ((URLClassLoader) cl).findResource("javax/servlet/http/HttpServlet.class") != null;
addClasses |= servletFound;
}
if (addClasses) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (int i = 0; i < urls.length; i++) {
String classFile = toFile(urls[i]);
if (classFile == null)
continue;
if (classPath.length() > 0)
classPath.append(File.pathSeparatorChar).append(classFile);
else
classPath.append(classFile);
}
}
if (jspFound && servletFound)
return classPath.toString();
}
cl = cl.getParent();
}
return System.getProperty("java.class.path");
}
public static final String toFile(URL url) {
if (url.getProtocol().indexOf("file") < 0)
return null;
String result = url.getPath();
if (result.charAt(0) == '/' && File.separatorChar == '\\')
result = result.substring(1);
return URLDecoder.decode(result);
}
// public static final int firstOccurrence(String s, String occur) {
//
// }
public static interface ThreadFactory {
Thread create(Runnable runnable);
}
public static final class ThreadPool {
static final int DEF_MAX_POOLED_THREAD = 20;
static final String ID = "Acme.Utils.ThreadPool";
public static final String MAXNOTHREAD = ID + ".maxpooledthreads";
protected static int counter;
protected ArrayList freeThreads;
protected HashMap busyThreads;
protected int maxThreads;
protected ThreadFactory threadFactory;
/**
* Creates a thread pool not queued with max number of threads defined in properties or DEF_MAX_POOLED_THREAD = 20
*
* @param Properties
* where property THREADSINPOOL gives max threads Note if THREADSINPOOL not integers, or negative then DEF_MAX_POOLED_THREAD used
*/
public ThreadPool(Properties properties, ThreadFactory threadfactory) {
try {
maxThreads = Integer.parseInt(properties.getProperty(MAXNOTHREAD));
if (maxThreads < 0)
maxThreads = DEF_MAX_POOLED_THREAD;
} catch (Exception e) {
maxThreads = DEF_MAX_POOLED_THREAD;
}
freeThreads = new ArrayList(maxThreads);
busyThreads = new HashMap(maxThreads);
this.threadFactory = threadfactory;
}
/**
* Assigns a new value for max threads
*
* @param int
* new value of max threads, can't be less than 2, but can be 0 If current number threads exceed the value, then extra thread will be
* discarded gracefully
*/
public void setMaxThreads(int newSize) {
if (newSize > 2 || newSize == 0)
maxThreads = newSize;
}
/**
* Returns setting for max number of threads
*
* @return int setting for max number of threads, doesn't reflect actual number of threads though
*/
public int getMaxThreads() {
return maxThreads;
}
/**
* Takes a new task for execution by a threads in pool will wait until free threads if number of threads reached max
*
* @param Runnable
* task for execution
*/
public void executeThread(Runnable runnable) {
PooledThread pt = null;
do {
synchronized (freeThreads) {
if (freeThreads.size() > 0)
pt = (PooledThread) freeThreads.remove(0);
}
if (pt != null && pt.isAlive() == false)
pt = null;
if (pt == null)
synchronized (busyThreads) {
if (busyThreads.size() < maxThreads || maxThreads == 0)
pt = new PooledThread();
}
if (pt == null)
synchronized (freeThreads) {
try {
freeThreads.wait();
} catch (InterruptedException ie) {
}
}
} while (pt == null);
pt.setName("-PooledThread: " + runnable);
pt.setRunner(runnable);
synchronized (busyThreads) {
busyThreads.put(pt, pt);
}
}
protected void finalize() throws Throwable {
synchronized (freeThreads) {
Iterator i = freeThreads.iterator();
while (i.hasNext())
((PooledThread) i.next()).interrupt();
}
synchronized (busyThreads) {
Iterator i = freeThreads.iterator();
while (i.hasNext())
((PooledThread) i.next()).interrupt();
}
super.finalize();
}
public String toString() {
if (freeThreads != null && busyThreads != null)
return ID + ": free threads " + freeThreads.size() + " busy threads " + busyThreads.size();
else
return ID + ": not initialized yet. " + super.toString();
}
class PooledThread implements Runnable {
Runnable runner;
boolean quit;
Thread delegateThread;
String id = ID + "(" + (counter++) + ")";
PooledThread() {
if (threadFactory != null)
delegateThread = threadFactory.create(this);
else
delegateThread = new Thread(this);
setName("-PooledThread: CREATED");
delegateThread.start();
}
public void setName(String name) {
delegateThread.setName(id + name);
}
public boolean isAlive() {
return delegateThread.isAlive();
}
synchronized public void run() {
do {
if (runner == null)
try {
this.wait();
} catch (InterruptedException ie) {
}
if (runner != null) {
try {
runner.run();
} catch (Throwable t) {
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
t.printStackTrace();
} finally {
runner = null;
}
int activeThreads = 0;
synchronized (busyThreads) {
busyThreads.remove(this);
activeThreads = busyThreads.size();
}
synchronized (freeThreads) {
if (freeThreads.size() + activeThreads > maxThreads)
break; // discard this thread
freeThreads.add(this);
delegateThread.setName(ID + "-PooledThread: FREE");
freeThreads.notify();
}
}
} while (!quit);
}
synchronized public void interrupt() {
quit = true;
delegateThread.interrupt();
}
synchronized void setRunner(Runnable runnable) {
if (runner != null)
throw new RuntimeException("Invalid worker thread state, current runner not null.");
runner = runnable;
this.notifyAll();
}
}
}
public static class DummyPrintStream extends PrintStream {
public DummyPrintStream() {
super(new OutputStream() {
public void write(int i) {
}
});
}
}
public static class SimpleBuffer {
byte[] buffer;
int fillPos;
byte[] emptyBuffer;
public SimpleBuffer() {
fillPos = 0;
setSize(COPY_BUF_SIZE);
}
public synchronized void setSize(int size) {
if (size < 0)
throw new IllegalArgumentException("Size can't be negative");
if (fillPos <= 0)
buffer = new byte[size];
else
throw new IllegalStateException("Can't resize buffer with already data in");
}
public synchronized int getSize() {
return buffer.length;
}
public synchronized byte[] put(byte[] data, int off, int len) {
//System.err.println("put in buff:" + len+", fp:"+fillPos);
if (buffer.length > fillPos + len) {
System.arraycopy(data, off, buffer, fillPos, len);
fillPos += len;
return getEmptyBuffer();
}
byte[] result = new byte[Math.max(fillPos + len - buffer.length, buffer.length)];
//System.err.println("fp:" + fillPos + ",bl:" + buffer.length + ",rl:" + result.length + ",l:" + len);
// fill result
int rfilled = 0;
if (fillPos < result.length) {
System.arraycopy(buffer, 0, result, 0, fillPos);
rfilled = result.length - fillPos;
System.arraycopy(data, off, result, fillPos, rfilled);
fillPos = 0;
//System.err.println("1rf:"+rfilled);
} else {
System.arraycopy(buffer, 0, result, 0, result.length);
System.arraycopy(buffer, result.length, buffer, 0, fillPos - result.length);
fillPos -= result.length;
rfilled = 0;
//System.err.println("qrf: 0");
}
if (rfilled < len) {
System.arraycopy(data, off + rfilled, buffer, fillPos, len - rfilled);
fillPos += len - rfilled;
//System.err.println("added to buf:"+(len - rfilled));
}
return result;
}
public synchronized byte[] get() {
//System.err.println("get fp: "+fillPos);
if (fillPos <= 0) {
return getEmptyBuffer();
}
byte[] result = new byte[fillPos];
System.arraycopy(buffer, 0, result, 0, fillPos);
fillPos = 0;
return result;
}
public synchronized void reset() {
//System.err.println("reset buf");
fillPos = 0;
}
private synchronized byte[] getEmptyBuffer() {
if (emptyBuffer == null)
emptyBuffer = new byte[0];
return emptyBuffer;
}
}
public static void main(String[] args) {
try {
System.out.println(args[0]);
System.out.println(canonicalizePath(args[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}