summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIang <iang@cacert.org>2010-09-09 10:47:50 +1000
committerIang <iang@cacert.org>2010-09-09 10:47:50 +1000
commitb623d4c6f7ecc3ad24874a0d5dc4f63c3b5ecfbb (patch)
tree7beec3ef60d740ba252f1bf6ce7ae8c5b49cf092
parentf9a2d53974b4d03e2211a5df76f7545af514874f (diff)
downloadcacert-birdshack-b623d4c6f7ecc3ad24874a0d5dc4f63c3b5ecfbb.tar.gz
cacert-birdshack-b623d4c6f7ecc3ad24874a0d5dc4f63c3b5ecfbb.tar.xz
cacert-birdshack-b623d4c6f7ecc3ad24874a0d5dc4f63c3b5ecfbb.zip
some util classes stolen from webfunds
-rw-r--r--Business Logic/trunk/src/org/cacert/birdshack/util/Equals.java251
-rwxr-xr-xBusiness Logic/trunk/src/org/cacert/birdshack/util/Hex.java624
-rw-r--r--Business Logic/trunk/src/org/cacert/birdshack/util/Junk.java532
-rwxr-xr-xBusiness Logic/trunk/src/org/cacert/birdshack/util/Panic.java95
4 files changed, 1502 insertions, 0 deletions
diff --git a/Business Logic/trunk/src/org/cacert/birdshack/util/Equals.java b/Business Logic/trunk/src/org/cacert/birdshack/util/Equals.java
new file mode 100644
index 0000000..2442f99
--- /dev/null
+++ b/Business Logic/trunk/src/org/cacert/birdshack/util/Equals.java
@@ -0,0 +1,251 @@
+/*
+ * Was webfunds.util.Example, contributed.
+ */
+package org.cacert.birdshack.util;
+
+import java.util.HashMap;
+
+/**
+ * A set of equality classes, for use in equals() methods.
+ *
+ * Similar role to org.apache.commons.lang.builder.EqualsBuilder ?
+ */
+public class Equals
+{
+ private Equals() {}
+
+ /**
+ * Only set this if you are running tests.
+ * Causes diags on "false" results.
+ */
+ public static void setNoisy() { _noisy = true; }
+ public static void resetNoisy() { _noisy = false; }
+ private static boolean _noisy = false;
+
+ /**
+ * @return True if they are the same object
+ * True if both null, false if only one is null.
+ */
+ public static boolean sameClass(Object one, Object two)
+ {
+ if (one == null && two == null)
+ return true;
+ if (one == null)
+ return noisy("Obj!=Obj 1st is <null>, 2nd: ", two);
+ if (two == null)
+ return noisy("Obj!=Obj 2nd is <null>, 1st: ", one);
+
+ // both != null
+ String classone = one.getClass().getName();
+ String classtwo = two.getClass().getName();
+ if( !classone.equals(classtwo) )
+ {
+ return noisy("Class!=Class " + classone + "!=" + classtwo);
+ }
+
+ return true;
+ }
+
+ /**
+ * @return true if the byte arrays are equal, false if not
+ */
+ public static boolean equals(byte[] b1, byte[] b2)
+ {
+ if (b1 == null && b2 == null)
+ return true ;
+ if (b1 == null)
+ return noisy("byte[] 1st is <null>");
+ if (b2 == null)
+ return noisy("byte[] 2nd is <null>");
+ int len1 = b1.length;
+ int len2 = b2.length;
+ if (len1 != len2)
+ return noisy("byte[] len "+len1+" (1st) != "+len2+" (2nd)");
+
+ // check if the data components are equivalent
+ String s1 = new String(b1);
+ String s2 = new String(b2);
+
+ if (! s1.equals(s2))
+ {
+ return noisy("byte[] contents differ (len " + len1 + ")" +
+ ONE + "\n" +
+ Hex.printable(b1) + "\n" +
+ TWO + "\n" +
+ Hex.printable(b2) + "\n" +
+ END);
+ }
+
+ return true ;
+ }
+
+ /**
+ * Equals for parent objects (handles null case).
+ * Works as long as you know at least one is the right Class
+ * (for example, was already type-checked).
+ * @param one, two are objects
+ * @return true if both null, false if one null,
+ * false if different class, else returns one.equals(two)
+ */
+ public static boolean equals(Object one, Object two)
+ {
+ if (one == null && two == null)
+ return true;
+ if (!Equals.sameClass(one, two)) // catches one == null
+ return noisy("Obj,Obj Not Equals(classes)!");
+
+ if (!one.equals(two))
+ return noisy("Obj,Obj Not Equals!", one, two);
+
+ return true;
+ }
+
+ /**
+ * Equals (list) for parent objects (handles null case).
+ *
+ * @param one, two are lists of Id
+ * @return true if both null, false if one null,
+ * else returns AND(one[].equals(two[]))
+ */
+ public static boolean equals(Object one[], Object two[])
+ {
+ if (one == null && two == null)
+ return true;
+ if (one == null)
+ return noisy("Obj[] 1st is <null> ( 2nd" + two.length + ")");
+ if (one == null)
+ return noisy("Obj[] 2nd is <null> ( 1st" + one.length + ")");
+ int len1 = one.length;
+ int len2 = two.length;
+ if (len1 != len2)
+ return noisy("Obj[] len "+len1+" (1st) != "+len2+" (2nd)");
+
+ for (int i = 0; i < len1; i++)
+ {
+ if (!equals(one[i], two[i]))
+ return noisy("Obj[" + i + "] not Equals!"); // dumped already
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Equals (list) for HashMaps of objects (handles null case).
+ *
+ * @param one, two are HashMaps
+ * @return true if both null, false if one null,
+ * else returns AND(one[].equals(two[]))
+ */
+ public static boolean equalsHashMap(HashMap one, HashMap two)
+ {
+ if (one == null && two == null)
+ return true;
+ if (one == null)
+ return noisy("HashMap first map is <Null>");
+ if (two == null)
+ return noisy("HashMap second map is <Null>");
+
+ java.util.Set x = one.keySet();
+ int len = x.size();
+ java.util.Set y = two.keySet();
+ if (len != y.size())
+ return noisy("HashMap sizes "+len+" (1st) != "+y.size()+" (2nd)");
+
+ java.util.Iterator keys = x.iterator();
+ while (keys.hasNext())
+ {
+ Object key = keys.next();
+ Object oneObj = one.get(key);
+ Object twoObj = two.get(key);
+ /*
+ * With HashMaps, have to check special case of
+ * value == null versus key not present.
+ */
+ if (oneObj == null && twoObj == null)
+ {
+ if (one.containsKey(key) != two.containsKey(key))
+ return noisy("HashMap has unmatched ref-><null>: " + key);
+ }
+ else if (oneObj == null)
+ {
+ return noisy("HashMap ref: " + key + " 1st null ", twoObj);
+ }
+ else if (twoObj == null)
+ {
+ return noisy("HashMap ref: " + key + " 2st null ", oneObj);
+ }
+ else
+ {
+ if (!oneObj.equals(twoObj))
+ {
+ return noisy("HashMap ref: " + key, oneObj, twoObj);
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Display a diag on the screen if noise is turned on.
+ */
+ private static boolean noisy(String s)
+ {
+ if (_noisy)
+ {
+ System.err.println(s);
+ }
+ return false;
+ }
+
+ /**
+ * Display a diag on the screen if noise is turned on.
+ */
+ private static boolean noisy(String s, Object o)
+ {
+ if (_noisy)
+ {
+ System.err.println(s);
+ dump(o);
+ }
+ return false;
+ }
+
+ /**
+ * Display a diag on the screen if noise is turned on.
+ */
+ private static void dump(Object o)
+ {
+ if (o == null)
+ {
+ System.err.println("Object: <null>");
+ return ;
+ }
+ System.err.println("Object: " + o.getClass().getName());
+ System.err.println(o.toString());
+ }
+
+ private static final String
+ ONE = "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ",
+ TWO = "2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ",
+ END = "^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ";
+
+ /**
+ * Display a diag on the screen if noise is turned on.
+ */
+ private static boolean noisy(String s, Object one, Object two)
+ {
+ if (_noisy)
+ {
+ System.err.println(s + "\n");
+ System.err.println(ONE);
+ dump(one);
+ System.err.println(TWO);
+ dump(two);
+ System.err.println(END);
+ }
+ return false;
+ }
+}
diff --git a/Business Logic/trunk/src/org/cacert/birdshack/util/Hex.java b/Business Logic/trunk/src/org/cacert/birdshack/util/Hex.java
new file mode 100755
index 0000000..b3ef02e
--- /dev/null
+++ b/Business Logic/trunk/src/org/cacert/birdshack/util/Hex.java
@@ -0,0 +1,624 @@
+/*
+ * Was webfunds.util.Hex, contributed.
+ */
+package org.cacert.birdshack.util;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * <p>Common hex-conversion and digest routines.</p>
+ *
+ * <p>These functions returns lower-case characters when converting to
+ * hexadecimal. Functions parsing hexadecimal can accept either case.</p>.
+ *
+ * <p>byte[] methods must not change the bytes as many Immutable
+ * classes rely on no changes being made.</p>
+ */
+public class Hex
+{
+ private static final char[] HEX_DIGITS = {
+ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
+ };
+
+
+ /** Static methods only. */
+ private Hex() {}
+
+
+ /**
+ * <p>
+ * Checks whether a String is made of Hex digits.
+ * </p><p>
+ * A null or empty argument is ok and returns false,
+ * as the intention is that all hex strings should
+ * be populated.
+ * </p>
+ */
+ public static boolean isHexAscii(String s)
+ {
+ if (null == s) // not logically correct, but is the intention
+ return false ;
+
+ if (s.length() == 0)
+ return false ;
+
+ /*
+ * For Hex, both upper and lower are ok.
+ */
+ for (int i = 0; i < s.length(); i++)
+ {
+ if (toDataNibble(s.charAt(i)) < 0)
+ return false;
+ }
+ return true ;
+ }
+
+
+ /**
+ * Must not change the bytes, many Immutable classes rely on this.
+ * Note that null is returned as the string NULL == "<NULL>".
+ */
+ public static String data2hex(byte[] data)
+ {
+ if (data == null)
+ return NULL;
+
+ int len = data.length;
+ StringBuffer buf = new StringBuffer(len*2);
+ for (int pos = 0; pos < len; pos++) {
+ buf.append(toHexDigit((data[pos]>>>4)&0x0F));
+ buf.append(toHexDigit((data[pos] )&0x0F));
+ }
+ return buf.toString();
+ }
+
+ public static String data2spacedhex(byte[] data)
+ {
+ return data2spacedhex(data, 1, " ");
+ }
+
+ /**
+ * Create a blocked hexadecimal string suitable for diagnostics
+ * bytes=2 and space=" " will give OpenPGP fingerprint style.
+ *
+ * @data some binary data to turn into a printable hex string
+ * @param bytes How many bytes in each block (1 gives 2 hex chars)
+ * @param space is the string between each block
+ */
+ public static String data2spacedhex(byte[] data, int bytes, String space)
+ {
+ if (data == null)
+ return NULL;
+ if (data.length == 0)
+ return "";
+ if (bytes < 1)
+ bytes = 1;
+ if (space == null)
+ space = " ";
+
+ int len = data.length;
+ StringBuffer buf = new StringBuffer(len * (space.length()+bytes*2) - 1);
+ int pos = 0;
+ while (pos < len)
+ {
+ if (pos != 0) buf.append(space);
+
+ int i = 0;
+ while (i < bytes && pos < len)
+ {
+ buf.append(toHexDigit((data[pos]>>>4)&0x0F));
+ buf.append(toHexDigit((data[pos] )&0x0F));
+ pos++; i++;
+ }
+ }
+ return buf.toString();
+ }
+
+ /*
+ * <p>
+ * Create a binary byte array with the hex characters converted.
+ * </p><p>
+ * Unchecked, GIGO.
+ * </p>
+ *
+ * @return always returns a byte array with the binary conversion,
+ * never returns null (empty on null argument)
+ * @see hex2dataOrNull for a checked method
+ */
+ public static byte[] hex2data(String str)
+ {
+ if (str == null)
+ return new byte[0] ;
+
+ int len = str.length(); // probably should check length
+ char hex[] = str.toCharArray();
+ byte[] buf = new byte[len/2];
+
+ for (int pos = 0; pos < len / 2; pos++)
+ {
+ byte hi = toDataNibble(hex[2*pos]);
+ byte lo = toDataNibble(hex[2*pos + 1]);
+
+ buf[pos] = (byte)( ((hi << 4) & 0xF0) | ( lo & 0x0F) );
+ }
+
+ return buf;
+ }
+
+ /**
+ * <p>
+ * Create a binary byte array with the hex characters converted.
+ * </p><p>
+ * Will fail (return null) if any non-hex chars are encountered,
+ * or if the string is empty or null.
+ * </p>
+ *
+ * @return the binary converted from the hex, else null if any bads found
+ * @see hex2dataOrNull for a unchecked method
+ */
+ public static byte[] hex2dataOrNull(String str)
+ {
+ if (str == null || str.length() == 0)
+ return null ;
+
+ int len = str.length();
+ char hex[] = str.toCharArray();
+ int buflen = len/2;
+ if (buflen * 2 != len) // whoops, need to check odd-length strings
+ return null;
+ byte[] buf = new byte[buflen];
+
+ for (int pos = 0; pos < buflen; pos++)
+ {
+ byte hi = toDataNibble(hex[2*pos]);
+ if (hi < 0) return null;
+ byte lo = toDataNibble(hex[2*pos + 1]);
+ if (lo < 0) return null;
+
+ buf[pos] = (byte)( ((hi << 4) & 0xF0) | ( lo & 0x0F) );
+ }
+
+ return buf;
+ }
+
+ /**
+ * Returns the the hex digit corresponding to the argument.
+ *
+ * @throws ArrayIndexOutOfBoundsException
+ * If the argument is not in range [0,15];
+ */
+ private static char toHexDigit(int i)
+ {
+ return HEX_DIGITS[i];
+ }
+
+ private static byte toDataNibble(char c)
+ {
+ if (('0' <= c) && (c <= '9' ))
+ return (byte)((byte)c - (byte)'0');
+ else if (('a' <= c) && (c <= 'f' ))
+ return (byte)((byte)c - (byte)'a' + 10);
+ else if (('A' <= c) && (c <= 'F' ))
+ return (byte)((byte)c - (byte)'A' + 10);
+ else
+ return -1;
+ }
+
+ static final String NULL = "<null>";
+
+ /**
+ * @return a byte array converted to hex, and truncated beyond some
+ * reasonable length, long enough to avoid debugging collisions.
+ */
+ public static String quick(byte[] b, int len)
+ {
+ if (b == null)
+ return NULL;
+ String middle = "..";
+ byte[] end = new byte[3];
+
+ if (b.length < (len + (middle.length() / 2) + end.length))
+ return data2hex(b);
+
+ byte[] start = new byte[len];
+ int i, j;
+ for (j = 0; j < start.length; j++)
+ start[j] = b[j];
+
+ for (j = 0, i = b.length - end.length; i < b.length; j++, i++)
+ end[j] = b[i];
+
+ return data2hex(start) + middle + data2hex(end);
+ }
+
+ /**
+ * @return a byte array converted to hex, and truncated beyond some
+ * reasonable length, long enough to avoid debugging collisions.
+ */
+ public static String quick(byte[] b)
+ {
+ return quick(b, 8);
+ }
+
+
+
+ /**
+ * Test if this is basic Ascii and printable.
+ *
+ * @return true if the string is printable
+ */
+ public static boolean isPrintable(byte[] data)
+ {
+ if (data == null)
+ return true;
+
+ int len = data.length;
+ for (int pos = 0; pos < len; pos++)
+ {
+ if (0x20 <= data[pos] && data[pos] <= 0xEF)
+ continue ;
+ if (data[pos] == '\r' || data[pos] == '\n' || data[pos] == '\t')
+ continue ;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Useful for printing descriptions, which are generally messages
+ * to the user. Sometimes they could be binary.
+ *
+ * @return a string that is printable, either the original, or a hex
+ * conversion of the original if funny chars found.
+ */
+ public static String description(byte[] data)
+ {
+ if (data == null)
+ return NULL;
+
+ int len = data.length;
+ if (isPrintable(data))
+ return new String(data);
+ else
+ return "[" + len + "] <<" + data2hex(data) + ">>";
+ }
+
+
+ /**
+ * Useful for printing printable strings, which may be binary,
+ * but most likely are not.
+ *
+ * @return a string that is printable, either the original, or a hex
+ * conversion of the original if funny chars found.
+ */
+ public static String printable(byte[] data)
+ {
+ if (data == null)
+ return NULL;
+
+ int len = data.length;
+ if (isPrintable(data))
+ return "{" + len + "} \"" + (new String(data)) + "\"";
+ else
+ return "[" + len + "] <<" + data2hex(data) + ">>";
+ }
+
+ /**
+ * Useful for printing descriptions, which can include binary.
+ * Does not change data ... but it used to !
+ * @return a string that is printable, with binaries dotted out.
+ */
+ public static String dotable(byte[] data)
+ {
+ if (data == null)
+ return NULL;
+
+ int len = data.length;
+ byte[] copy = new byte[len];
+ for (int pos = 0; pos < len; pos++)
+ {
+ if (0x20 <= data[pos] && data[pos] <= 0xEF)
+ copy[pos] = data[pos];
+ else if (data[pos] == '\n' || data[pos] == '\t')
+ copy[pos] = data[pos];
+ else if (data[pos] == '\r')
+ copy[pos] = (byte)'\\';
+ else
+ copy[pos] = (byte)'.';
+ }
+ return new String(copy);
+ }
+
+ private static final byte[] dots = "...".getBytes();
+
+ /**
+ * Useful for printing errors, which can include binary.
+ * Turns any wierdness into hex numbers, leaves US-ASCII
+ * printable.
+ * Kind to estimated callers, corrects bounds on from, to.
+ *
+ * @param data the ascii text with some dodgy chars
+ * @param from the start position, made 0 if negative
+ * @param to the end position, made the end if greater than length
+ * @return a string that is printable, with binary hexed.
+ */
+ public static String hexable(byte[] data, int from, int to)
+ {
+ if (data == null)
+ return NULL;
+ if (from < 0) from = 0;
+ if (to > data.length) to = data.length;
+
+ ByteArrayOutputStream bais = new ByteArrayOutputStream();
+
+ try {
+ if (from > 0) bais.write(dots);
+
+ int len = to;
+ for (int pos = from; pos < len; pos++)
+ {
+ int b = data[pos] & 0xFF;
+ if (0x20 <= b && b < 0x7F)
+ bais.write(b) ;
+ else if (b == '\n')
+ bais.write("\\n\n".getBytes());
+ else if (b == '\r')
+ bais.write("\\r\n".getBytes());
+ else if (b < 0x20)
+ {
+ String s = "\0x" + toString((byte)b);
+ bais.write(s.getBytes());
+ }
+ else
+ {
+ String s = "<" + toString((byte)b) + ">";
+ bais.write(s.getBytes());
+ }
+ }
+ if (to < data.length) bais.write(dots);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ return new String( bais.toByteArray() );
+ }
+
+ /**
+ * Useful for printing errors, which can include binary.
+ * Turns any wierdness into hex numbers, leaves US-ASCII
+ * printable.
+ *
+ * @return a string that is printable, with binary hexed.
+ */
+ public static String hexable(byte[] data)
+ {
+ if (data == null)
+ return NULL;
+
+ return hexable(data, 0, data.length);
+ }
+
+ /**
+ * Useful for printing errors, which can include binary.
+ * Turns any wierdness into hex numbers, leaves US-ASCII
+ * printable.
+ *
+ * @return a string that is printable, with binary hexed.
+ */
+ public static String hexable(String s)
+ {
+ if (s == null)
+ return NULL;
+
+ try {
+ byte[] data = s.getBytes("UTF-8");
+ return hexable(data);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+
+ /**
+ * Converts the given byte to an unsigned 2-character hex string.
+ */
+ public static String toString(byte x) {
+ char[] cs = new char[2];
+ cs[0] = toHexDigit((x >>> 4) & 0xF);
+ cs[1] = toHexDigit((x ) & 0xF);
+ return new String(cs);
+ }
+
+
+ /**
+ * Converts the given int to an unsigned 8-character hex string.
+ */
+ public static String toString(int x) {
+ char[] cs = new char[8];
+ for(int i=7; i>=0; i--, x>>>=4)
+ cs[i] = toHexDigit(x & 0xF);
+ return new String(cs);
+ }
+
+
+ /**
+ * Converts the given long to an unsigned 16-character hex string.
+ */
+ public static String toString(long x) {
+ char[] cs = new char[16];
+ for(int i=15; i>=0; i--, x>>>=4)
+ cs[i] = toHexDigit((int)x & 0xF);
+ return new String(cs);
+ }
+
+
+
+ /**
+ * Make a copy of the raw bytes, for an immutable Id get method.
+ * (This is similar to clone(), but it handles null, and there
+ * is some change/bug where byte[].clone() has become protected.
+ * Also, byte[].clone() doesn't work for 1.4.1 ?)
+ *
+ * @param the original bytes (can be null)
+ * @return the bytes copied (null is returned if null passed)
+ */
+ public static byte[] copy(byte[] buf)
+ {
+ if (buf == null)
+ return null;
+ int len = buf.length;
+ byte[] temp = new byte[len];
+ System.arraycopy(buf, 0, temp, 0, len);
+ return temp;
+ }
+
+
+ /**
+ * <p>
+ * Convert this string representation into the bytes.
+ * Strips any hint strings that might be present in
+ * debug outputs, etc. The hint strings are of the
+ * the form "SHA1#abc123..." or perhaps "contractid:123abc..."
+ * </p><p>
+ * The resultant bytes should be the underlying message digest
+ * binary bytes, and suitable for reconstruction into another
+ * form of Id. This is essential for crossing package boundaries
+ * where the packages themselves cannot agree on dependencies.
+ * </p><p>
+ * The strings come from many different higher level applications
+ * that use strings (Lucaya, servers, etc). Users tend to
+ * pass this format across to things like payment dialogs.
+ * </p>
+ *
+ * @return a byte array of indeterminate length, or null if no
+ * hex string was found, or there were garbage chars in it
+ */
+ public static byte[] getBytesFromIdString(String s)
+ {
+ if (s == null)
+ return null;
+ int start = s.indexOf('#');
+ if (start == -1)
+ start = s.indexOf(':');
+ if (start >= 0)
+ s = s.substring(start + 1);
+ byte[] bytes = hex2dataOrNull(s);
+ return bytes;
+ }
+
+ /**
+ * Boring utility that saves writing it all the time.
+ * Turn 4 bytes into a hashcode, no pertubations.
+ * @throws Boom if not 4 bytes available in b or null
+ */
+ public static int bytes2hashCode(byte[] b)
+ {
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+ }
+
+ /**
+ * A quick function for diags, long enough to
+ * avoid collisions, but short enough to print out.
+ * Does not change the bytes.
+ *
+ * @param buf is the bytes to convert to hex
+ * @param number is minimum of bytes (chars is 2*(bytes+1))
+ */
+ public static String diag(byte[] buf, int number)
+ {
+ if (buf.length <= (number+1))
+ return Hex.data2hex(buf);
+
+ byte[] bbb = new byte[number];
+ for (int j = 0; j < bbb.length; j++)
+ bbb[j] = buf[j];
+
+ return Hex.data2hex(bbb) + "..";
+ }
+
+
+
+
+
+
+ ////////////////////////////////
+ // //
+ // T E S T C O D E //
+ // //
+ ////////////////////////////////
+
+
+
+ public static void main(String args[])
+ throws Exception
+ {
+ args = null;
+ madeUpTest();
+ dataTest();
+ System.err.println("Tests Passed.");
+ System.exit(0);
+ }
+
+ /**
+ * Test some made up strings for explicit conversion to bytes.
+ */
+ public static void madeUpTest()
+ throws Exception
+ {
+ String s = "c001d00d";
+ byte b[] = { (byte)0xC0, (byte)0x01, (byte)0xD0, (byte)0x0D };
+
+ System.err.println("bytes: " + data2spacedhex(b));
+ String test = data2hex(b).toLowerCase();
+ if (!test.equals(s))
+ throw new Exception("data2hex " + s + " failed: " + test);
+ System.err.println("string: " + s);
+
+ byte ttt[] = hex2data(s);
+ byte uuu[] = hex2dataOrNull(s);
+ if (uuu == null)
+ throw new Exception("hex2dataOrNull " + s + " failed: null");
+ // ttt[2] = (byte)0xFA; // test
+ for (int i = 0; i < b.length; i++)
+ {
+ if (ttt[i] != b[i])
+ throw new Exception("hex2data " + s + " failed: "
+ + data2hex(ttt) + " (byte " + i + ")");
+ if (uuu[i] != b[i])
+ throw new Exception("hex2dataOrNull " + s + " failed: "
+ + data2hex(ttt) + " (byte " + i + ")");
+ }
+
+ s += "-0";
+ if (hex2dataOrNull(s) != null)
+ throw new Exception("hex2dataOrNull did not return null on " + s);
+ if (hex2data(s) == null)
+ throw new Exception("hex2data returned null on " + s + "???");
+
+ System.err.println("made up data tested.");
+ }
+
+ public static void dataTest()
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ String s = Junk.string();
+ System.err.println(i + ": " + s);
+ System.err.println(data2spacedhex(s.getBytes(), i, ".."));
+ cycle(s);
+ }
+ }
+
+ public static void cycle(String s)
+ {
+ byte[] b = s.getBytes();
+ String test = data2hex(b).toLowerCase();
+ byte[] back = hex2data(test);
+ String result = new String(back);
+ if (!result.equals(s))
+ throw new RuntimeException("Failed!" +
+ "\nstart: " + s +
+ "\n hex : " + test +
+ "\n back: " + result);
+ }
+
+}
diff --git a/Business Logic/trunk/src/org/cacert/birdshack/util/Junk.java b/Business Logic/trunk/src/org/cacert/birdshack/util/Junk.java
new file mode 100644
index 0000000..708fb2c
--- /dev/null
+++ b/Business Logic/trunk/src/org/cacert/birdshack/util/Junk.java
@@ -0,0 +1,532 @@
+/*
+ * Was webfunds.util.Example, contributed.
+ */
+package org.cacert.birdshack.util;
+
+import java.math.BigInteger;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A set of example, junk data fields, used for example() methods.
+ *
+ * NONE of these routines are cryptographically secure,
+ * in fact they are not even vaguely random, and only
+ * intended for class unit tests.
+ *
+ * Also, a set of reflection methods for constructing
+ * unit tests that read, write and compare examples.
+ */
+public class Junk
+{
+ private Junk() {}
+
+
+ private static long seed = 0;
+
+ /**
+ * Return data byte(s) that is rubbish.
+ *
+ * All Junk routines are built upon exampleByte().
+ *
+ * @return an example byte
+ */
+ public static byte exampleByte()
+ {
+ if (seed == 0)
+ seed = System.currentTimeMillis();
+
+ seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
+ return (byte) (seed >>> (48 - 8));
+
+ /*
+ * Another one, apparently called a TYPE_0 generator:
+ * i' = (i * 1103515245 + 12345) & 0x7fffffff
+ */
+ }
+
+ /** @return example boolean, true or false */
+ public static boolean exampleBool()
+ {
+ if (seed == 0)
+ seed = System.currentTimeMillis();
+
+ return (exampleByte() & 0x01) == 0x01;
+ }
+
+ /** @return example int, between min and max */
+ public static int exampleInt(int min, int max)
+ {
+ if (min == max)
+ return min;
+
+ if (min > max) // swap
+ {
+ int x = min;
+ min = max;
+ max = x;
+ }
+ long min2 = min;
+ long max2 = max;
+ long offset = 0;
+ if (min2 < 0) // move numbers to 0..n
+ {
+ offset = (-min2);
+ max2 += offset;
+ min2 = 0;
+ }
+ long l = exampleLong();
+ if (l < 0)
+ l = -l;
+ // whoops this was limited it to max-1
+ int ex = (int) ( ( l % (max2 - min2 + 1) ) + min2 - offset );
+ if (ex < min || ex > max)
+ throw new RuntimeException("ex out of min,max: "+
+ ex+" "+min+","+max+ " ["+min2+","+max2+"] ("+l+","+offset+")" );
+ return ex;
+ }
+
+ /**
+ * This returns a long that is biased to small numbers.
+ * @see exampleTimeMillis() for a time biased example long,
+ * @see exampleBigLong() for an unbiased long.
+ * @return example long, made of 8 example bytes, biased to small
+ */
+ public static long exampleLong()
+ {
+ long l = 0;
+ for (int i = (exampleByte() & 0x07); i >=0; i--)
+ { // kilted towards low numbers
+ l <<= 8;
+ l |= exampleByte() & 0xFF;
+ }
+ return l ;
+ }
+
+ /**
+ * This returns a long that is biased to time.
+ public static long exampleTimeMillis()
+ {
+ return BaseTime.ex();
+ }
+ */
+
+ /** @return junk char, made of 2 example bytes */
+ public static char junkChar()
+ {
+ int i = ((exampleByte() & 0xFF) << 8) | (exampleByte() & 0xFF);
+ return (char)i ;
+ }
+
+// static java.nio.charset.Charset utf8charset;
+// static java.nio.charset.CharsetDecoder utf8decoder;
+// static java.nio.charset.CharsetEncoder utf8encoder;
+//
+// /** @return example char, made of 2 example bytes, should be good UTF-8 */
+// static
+// {
+// try {
+// utf8charset = java.nio.charset.Charset.forName("UTF-8");
+// utf8encoder = utf8charset.newEncoder();
+// // utf8encoder.substitute(null); // throw exceptions on duds.
+//
+// if (!utf8charset.canEncode())
+// throw new Panic("can't encode?");
+//
+// int c;
+// char[] chars = new char[1];
+// for (int i = 0; i < 10 ; i++)
+// {
+// c = i << 8;
+// System.err.print("" + c);
+// for (int j = 20; j < 90 ; j += 10)
+// {
+// char c2 = (char)(c | j);
+// chars[0] = c2;
+// String s = new String(chars);
+// dumpUTF(s);
+// }
+// System.err.println("!");
+// }
+//
+// } catch (Exception ex) {
+// throw new Panic(ex);
+// }
+// }
+
+ /*
+ * This returns a hopefully printable UTF-8 char.
+ * Unprintables in US-ASCII are eliminated,
+ * as are illegals in UTF-8 ('\uFFFE', '\uFFFF').
+ */
+ public static char exampleChar()
+ {
+ int i = 0;
+ char c;
+ while (true)
+ {
+ c = junkChar();
+ if (c < '\u0020') // no, \r doesn't count :)
+ continue;
+ if (c == '\u00FF') // nor del
+ continue;
+ if (c == '\uFFFE' || c == '\uFFFF') // illegal in UTF-8
+ continue;
+
+ break;
+ }
+ return c;
+ }
+
+ /** @return example long, made of 8 example bytes, unbiased */
+ public static long exampleBigLong()
+ {
+ long l = 0;
+ for (int i = 0; i < 8; i++)
+ { // no bias
+ l <<= 8;
+ l |= exampleByte() & 0xFF;
+ }
+ return l ;
+ }
+
+ /** @return byte array with len example bytes in it, never null */
+ public static byte[] data(int len)
+ {
+ byte[] b = new byte[len];
+
+ for (int i = len - 1; i >=0; i--)
+ {
+ b[i] = exampleByte();
+ }
+ return b ;
+ }
+
+ /** @return unprintable byte array from 0 to 32 bytes long, never null */
+ public static byte[] data()
+ {
+ int len = exampleByte() & 0x1F;
+ return data(len);
+ }
+
+ /** @return unprintable byte array from 0 to 16 bytes long, may be null */
+ public static byte[] dataWithNull()
+ {
+ int b = exampleByte();
+ if ((b & 0xF0) == 0)
+ return null;
+ int len = b & 0x0F;
+ return data(len);
+ }
+
+ /** @return unprintable byte array from min to max bytes long */
+ public static byte[] data(int min, int max)
+ {
+ int len = exampleInt(min, max);
+ return data(len);
+ }
+
+
+
+ /** @return printable string with some chars made into funny characters */
+ public static void dumpUTF(String s)
+ {
+ try {
+ byte[] bytes = s.getBytes("UTF-8");
+ System.err.print(" " + s + " ");
+ System.err.write(bytes, 0, bytes.length);
+ } catch (Exception ex) {
+ throw new Error("UTFF");
+ }
+ }
+
+ /** @return printable string with some chars made into funny characters */
+ public static String makeUTF(String s)
+ {
+ char[] chars = s.toCharArray();
+ int len = chars.length;
+ for (int i = 0; i < len; i++)
+ {
+ if ((exampleByte() & 0x03) == 0)
+ chars[i] = exampleChar();
+ }
+ String s2 = new String(chars);
+ // dumpUTF(s2);
+ return s2;
+ }
+
+ /** @return printable string len bytes long, never null */
+ public static String string(int len)
+ {
+ byte[] data = data(len);
+ return Hex.data2hex(data);
+ }
+
+ /** @return printable string from min to max bytes long */
+ public static String string(int min, int max)
+ {
+ int len = exampleInt(min, max);
+ return string(len);
+ }
+
+ /** @return valid Identifier for the Resources */
+ public static String identifier()
+ {
+ int len = exampleInt(1, 7);
+ return string(len);
+ }
+
+ /** @return printable string from 0 to 32 bytes long, never null */
+ public static String string()
+ {
+ int len = exampleByte() & 0x1F;
+ return string(len);
+ }
+
+ /** @return printable string from 0 to 8 words long, never null */
+ public static String words()
+ {
+ int b = exampleByte() & 0x07;
+ String s = "";
+ for (int i = 0; i < b; i++)
+ {
+ if (!s.equals("") || exampleBool())
+ s += whitespace();
+ s += string(1, 4);
+ }
+ if (exampleBool())
+ s += whitespace();
+ return s;
+ }
+
+ /** @return printable string from 0 to 3 lines of words(), never null */
+ public static String lines()
+ {
+ int b = exampleByte() & 0x03;
+ String s = "";
+ for (int i = 0; i < b; i++)
+ {
+ if (!s.equals("") || (exampleByte() & 0x03) == 0x03)
+ s += "\n";
+ s += words();
+ }
+ if ((exampleByte() & 0x03) == 0x03)
+ s += "\n";
+ return s;
+ }
+
+
+ /**
+ * 0, 1, 2 chars (weighted to 1) of spaces (common) and tabs (rare)
+ * @return some optional white-space
+ */
+ public static String whitespace()
+ {
+ int b = exampleByte();
+ int len = b & 0x03;
+ if (len == 0x03)
+ len = 1;
+ b >>= 2;
+ String s = "";
+ while (len-- > 0)
+ {
+ if ((b & 0x03) == 0x03)
+ s += " "; // tab
+ else
+ s += " ";
+ b >>= 2;
+ }
+ return s;
+ }
+
+
+
+ /**
+ * @return a BigInteger that is positive and made of len junk bytes
+ * @param len bytes, where len >= 1
+ */
+ public static BigInteger examplePosBigInt(int len)
+ {
+ byte[] data = data(len);
+ data[0] &= 0x7F; // so always positive
+ return new BigInteger(data);
+ }
+
+ /**
+ * @return something from 8 bytes to 16 bytes worth of big int.
+ */
+ public static BigInteger examplePosBigInt()
+ {
+ int len = exampleByte() & 0x07;
+ len |= 0x08;
+ return examplePosBigInt(len);
+ }
+
+
+
+/****************************************************
+ *
+ * These methods are used in conjunction with
+ * above example code to construct encode-decode
+ * tests. They may be better off in another class.
+ *
+ */
+
+ /**
+ * Return the object decoded from the byte stream,
+ * which came from an encode of the same object.
+ *
+ * The class named must follow the WireObject convention
+ * by including an InputStream constructor.
+ *
+ * @throws RuntimeException if the method could not be called
+ * @return an example of the named class.
+ */
+ public static Object decodeFromBytes(String fullclassname, byte[] contents)
+ {
+ Class clazz = getKnownClass(fullclassname);
+ ByteArrayInputStream bais = new ByteArrayInputStream(contents);
+
+ return decodeFromBytes(clazz, bais);
+ }
+
+ /**
+ * Return the object decoded from the byte stream,
+ * which came from an encode of the same object.
+ *
+ * The class supplied must follow the WireObject convention
+ * by including an InputStream constructor.
+ *
+ * @throws RuntimeException if the method could not be called
+ * @return an example of the named class.
+ */
+ public static Object decodeFromBytes(Class clazz, byte[] contents)
+ {
+ ByteArrayInputStream bais = new ByteArrayInputStream(contents);
+
+ return decodeFromBytes(clazz, bais);
+ }
+
+ /**
+ * Return the object decoded from the byte stream,
+ * which came from an encode of the same object.
+ *
+ * The class supplied must follow the WireObject convention
+ * by including an InputStream constructor.
+ *
+ * @throws RuntimeException if the method could not be called
+ * @return an example of the named class.
+ */
+ public static Object decodeFromBytes(Class clazz, InputStream is)
+ {
+// InputStream now being replaced by WireInputStream
+ Class[] arguments = new Class[1];
+ Class inputStream = getKnownClass("java.io.InputStream");
+ arguments[0] = inputStream;
+ String name = clazz.getName();
+
+ // there should be a way to get the byte[] constructor
+ // based on the Byte.TYPE class, but the javadoc doesn't
+ // say how to make an array class....
+
+ Constructor con;
+ try {
+ con = clazz.getConstructor(arguments);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("no contructor(InputStream) in " + name);
+ }
+
+ Object[] initArgs = new Object[1];
+ initArgs[0] = (Object)is;
+ Object obj;
+ try {
+ obj = con.newInstance(initArgs);
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ throw new RuntimeException("bad constructor(IS) in "+name+": "+e);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ throw new RuntimeException("bad constructor(IS) in "+name+": "+e);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ throw new RuntimeException("bad constructor(IS) in "+name+": "+e);
+ }
+
+ return obj;
+ }
+
+ /**
+ * The class named must follow the WebFunds testing convention
+ * by including the static example() method.
+ *
+ * @throws RuntimeException if the method could not be called
+ * @return an example of the named class.
+ */
+ public static Object getExample(String fullclassname)
+ {
+ Class clazz = getKnownClass(fullclassname);
+ Method meth;
+ try {
+ meth = clazz.getMethod("example", new Class[]{}); // zero args
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("no example() in " + fullclassname);
+ }
+
+ Object obj;
+ try {
+ obj = meth.invoke(null, new Object[]{}); // zero args
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ throw new RuntimeException("bad example() "+fullclassname+":\n"+e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("bad example() "+fullclassname+":\n"+e);
+ } catch (Exception e) { // reflection throws lots of rubbish
+ e.printStackTrace();
+ throw new RuntimeException("bad example() "+fullclassname+":\n"+e);
+ }
+
+ if (obj == null)
+ throw new RuntimeException("null example() from " + fullclassname +
+ ": " + clazz);
+
+ return obj;
+ }
+
+ /**
+ * This method just hides the exception.
+ * @throws RuntimeException if the class doesn't exist
+ * @return the Class for this name
+ */
+ public static Class getKnownClass(String fullclassname)
+ {
+ Class byname;
+ try
+ {
+ byname = Class.forName(fullclassname);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException("No class for " + fullclassname + "\n" +
+ "check CLASSPATH or name or source.");
+ }
+ return byname;
+ }
+
+
+
+/****************************************************
+ *
+ * Quick Test Method.
+ */
+
+ public static void main(String[] arg)
+ {
+ System.out.println("string(): " + string());
+ System.out.println("words(): " + words());
+ System.out.println("lines(): -----\n" + lines() + "------");
+ }
+}
diff --git a/Business Logic/trunk/src/org/cacert/birdshack/util/Panic.java b/Business Logic/trunk/src/org/cacert/birdshack/util/Panic.java
new file mode 100755
index 0000000..d51a9ba
--- /dev/null
+++ b/Business Logic/trunk/src/org/cacert/birdshack/util/Panic.java
@@ -0,0 +1,95 @@
+/*
+ * Was webfunds.util.Panic, contributed.
+ */
+package org.cacert.birdshack.util;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Panic is thrown to force an unclean exit of the JVM when a fatal
+ * error occurs. This exception should not (and can not) be caught.
+ *
+ * Upon construction, Panic logs an message to Log4J at ERROR level. The
+ * message includes a stack trace (either obtained from the passed-in
+ * Throwable or generated by the Panic class itself). Panic then calls
+ * the Log4J Logger.shutdown() method and does a System.exit(1).
+ *
+ * Panic extends Error because it should not be caught. (I think it
+ * should extend Throwable but Jikes does not allow that. --JCvG)
+ *
+ * @author Jeroen C. van Gelderen (gelderen@webfunds.org)
+ * @version $Id: Panic.java,v 1.15 2005/02/02 19:45:57 iang Exp $
+ */
+public final class Panic extends Error {
+
+ private static final Logger _logger =
+ Logger.getInstance(Panic.class.getName());
+
+ private static boolean handle = true;
+
+ /**
+ * Test classes can call this if they wish to catch
+ * Panic when they expect it to happen.
+ * This will disable the internal exit.
+ */
+ public static void disableHandling()
+ {
+ _logger.warn( "Panic.disableHandling() - this must be a test class.");
+ handle = false;
+ }
+
+
+ /** Panic with a default message. */
+ public Panic() {
+ this("unknown cause", null);
+ }
+
+
+ /** Panic with a message. A stack trace will be generated. */
+ public Panic(String msg) {
+ this(msg, null);
+ }
+
+
+ /** Panic with just a Throwable. */
+ public Panic(Throwable cause) {
+ this(cause.getMessage(), cause);
+ }
+
+
+ /** Panic with a message and the Throwable that caused the panic. */
+ public Panic(String msg, Throwable cause) {
+
+ super("PANIC: " + msg + "\nPanic from this cause:\n", cause);
+
+ if (handle)
+ {
+ if( cause != null ) {
+ _logger.error( this.getMessage(), cause );
+ } else {
+ // obtain a StackTrace and log it
+ Throwable t = new Throwable().fillInStackTrace();
+ _logger.error( this.getMessage() +
+ "\n(Stack trace generated by Panic)", t );
+ }
+
+ _logger.error("\nGoing to call System.exit(1)...");
+ Logger.shutdown();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * A stopgap measure until Java 1.5.
+ * When assert becomes available, change all the lines to
+ * use the direct call.
+ */
+ public static void assert2(boolean asserted)
+ {
+ if (asserted)
+ return ;
+
+ throw new Panic("assert failed.");
+ }
+}