import java.util.*; import java.util.zip.*; import java.util.List; import java.util.regex.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.table.*; import java.io.*; import java.net.*; import java.lang.reflect.*; import java.lang.ref.*; import java.lang.management.*; import java.security.*; import java.security.spec.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.imageio.*; import java.math.*; public class main { static Highest highest = new Highest(); static class Infos { Map chars; boolean debug; } static interface Algorithm { String recognizeWord(BWImage img, Infos infos); } static Infos infos; static Map words; public static void main(String[] args) throws Exception { load("highest"); } static boolean suck(String desc, Algorithm algorithm) { print("Best score: " + highest.score); if (highest.update(desc, scoreAlgorithm(algorithm))) { save("highest"); return true; } return false; } static synchronized double scoreAlgorithm(Algorithm algorithm) { if (infos == null) { infos = new Infos(); infos.chars = mapValues("charRangeToChar", loadGlyphSheet("#1004645")); } if (words == null) words = loadGlyphSheet("#1004649"); printStructure(values(words)); double score = 0, total = 0; for (BWImage img : keys(words)) { infos.debug = false; String w; try { w = algorithm.recognizeWord(img, infos); } catch (Throwable e) { w = exceptionToStringShort(e); silentException(e); } String real = words.get(img); print(real + " => " + w); ++total; if (eq(real, w)) { ++score; print(" ok!"); } else { print(" oops..."); if (l(real) != 0) { int lev = leven(real, w); double s = max(0, 1-lev/(double) l(real)); print("score: " + s + " (lev: " + lev + ")"); score += s; } // Do it again for debug output (could be skipped if needed) infos.debug = true; try { algorithm.recognizeWord(img, infos); } catch (Throwable _e) {} } } print(); print("SCORE: " + formatDouble(score, 1) + "/" + formatDouble(total, 1)); if (lastSilentException() != null) printStackTrace(lastSilentException()); return score*100/total; } public static String formatDouble(double d, int digits) { String format = "0."; for (int i = 0; i < digits; i++) format += "#"; return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d); } static void printStackTrace(Throwable e) { // we go to system.out now - system.err is nonsense print(getStackTrace(e)); } static void printStackTrace() { printStackTrace(new Throwable()); } static int max(int a, int b) { return Math.max(a, b); } static long max(int a, long b) { return Math.max((long) a, b); } static long max(long a, long b) { return Math.max(a, b); } static double max(int a, double b) { return Math.max((double) a, b); } static int max(Collection c) { int x = Integer.MIN_VALUE; for (int i : c) x = max(x, i); return x; } static double max(double[] c) { if (c.length == 0) return Double.MIN_VALUE; double x = c[0]; for (int i = 1; i < c.length; i++) x = Math.max(x, c[i]); return x; } static byte max(byte[] c) { byte x = -128; for (byte d : c) if (d > x) x = d; return x; } static Set keys(Map map) { return map.keySet(); } static Set keys(Object map) { return keys((Map) map); } static Map mapValues(Object func, Map map) { Map m = similarEmptyMap(map); for (Object key : keys(map)) m.put(key, callF(func, map.get(key))); return m; } static Collection values(Map map) { return map.values(); } static int leven(String s, String t) { // degenerate cases if (s.equals(t)) return 0; if (s.length() == 0) return t.length(); if (t.length() == 0) return s.length(); // create two work vectors of integer distances int[] v0 = new int[t.length() + 1]; int[] v1 = new int[t.length() + 1]; // initialize v0 (the previous row of distances) // this row is A[0][i]: edit distance for an empty s // the distance is just the number of characters to delete from t for (int i = 0; i < v0.length; i++) v0[i] = i; for (int i = 0; i < s.length(); i++) { // calculate v1 (current row distances) from the previous row v0 // first element of v1 is A[i+1][0] // edit distance is delete (i+1) chars from s to match empty t v1[0] = i + 1; // use formula to fill in the rest of the row for (int j = 0; j < t.length(); j++) { int cost = s.charAt(i) == t.charAt(j) ? 0 : 1; v1[j + 1] = Math.min(Math.min(v1[j] + 1, v0[j + 1] + 1), v0[j] + cost); } // copy v1 (current row) to v0 (previous row) for next iteration /*for (int j = 0; j < v0.length; j++) v0[j] = v1[j];*/ // swap arrays int[] v = v1; v1 = v0; v0 = v; } // for array copying: // return v1[t.length()]; // for array swapping: return v0[t.length()]; } static void save(String varName) { saveLocally(varName); } static void save(String progID, String varName) { saveLocally(progID, varName); } static volatile int silentException_count; static volatile Throwable silentException_lastException; static void silentException(Throwable e) { ++silentException_count; // yeah it's not atomic :) silentException_lastException = e; } static Throwable lastSilentException() { return silentException_lastException; } static String charRangeToChar(String s) { try { int i = s.indexOf('-'); if (i > 0) s = substring(s, 0, i); return str((char) hexToInt(s)); } catch (Throwable _e) { return "[" + s + "]"; } } static String exceptionToStringShort(Throwable e) { e = getInnerException(e); String msg = unnull(e.getMessage()); if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0) return baseClassName(e) + ": " + msg; else return msg; } static boolean eq(Object a, Object b) { if (a == null) return b == null; if (a.equals(b)) return true; if (a instanceof BigInteger) { if (b instanceof Integer) return a.equals(BigInteger.valueOf((Integer) b)); if (b instanceof Long) return a.equals(BigInteger.valueOf((Long) b)); } return false; } static int l(Object[] a) { return a == null ? 0 : a.length; } static int l(byte[] a) { return a == null ? 0 : a.length; } static int l(int[] a) { return a == null ? 0 : a.length; } static int l(float[] a) { return a == null ? 0 : a.length; } static int l(char[] a) { return a == null ? 0 : a.length; } static int l(Collection c) { return c == null ? 0 : c.size(); } static int l(Map m) { return m == null ? 0 : m.size(); } static int l(CharSequence s) { return s == null ? 0 : s.length(); } static int l(Object o) { return l((List) o); // incomplete } static volatile StringBuffer local_log = new StringBuffer(); // not redirected static volatile StringBuffer print_log = local_log; // might be redirected, e.g. to main bot // in bytes - will cut to half that static volatile int print_log_max = 1024*1024; static volatile int local_log_max = 100*1024; //static int print_maxLineLength = 0; // 0 = unset static boolean print_silent; // total mute if set static void print() { print(""); } // slightly overblown signature to return original object... static A print(A o) { if (print_silent) return o; String s = String.valueOf(o) + "\n"; // TODO if (print_maxLineLength != 0) StringBuffer loc = local_log; StringBuffer buf = print_log; int loc_max = print_log_max; if (buf != loc && buf != null) { print_append(buf, s, print_log_max); loc_max = local_log_max; } if (loc != null) print_append(loc, s, loc_max); System.out.print(s); return o; } static void print(long l) { print(String.valueOf(l)); } static void print(char c) { print(String.valueOf(c)); } static void print_append(StringBuffer buf, String s, int max) { synchronized(buf) { buf.append(s); max /= 2; if (buf.length() > max) try { int newLength = max/2; int ofs = buf.length()-newLength; String newString = buf.substring(ofs); buf.setLength(0); buf.append("[...] ").append(newString); } catch (Exception e) { buf.setLength(0); } } } static void load(String varName) { readLocally(varName); } static void load(String progID, String varName) { readLocally(progID, varName); } static Map loadGlyphSheet(String sheetID) { List images = unclusterImages(loadBWImage(sheetID)); if (images == null) throw fail("Unclustering failed!?"); images = map(new Object() { Object get(BWImage i) { return removeGrayBorder(i) ; } public String toString() { return "removeGrayBorder(i)"; }}, images); Map map = litorderedmap(); for (BWImage img : images) { { if (img == null) continue; } Pair p = unsubtitle(img); if (p == null) print("Unsubtitle failed"); else map.put(p.a, p.b); } return map; } static void printStructure(String prefix, Object o) { print(prefix + structure(o)); } static void printStructure(Object o) { print(structure(o)); } static LinkedHashMap litorderedmap(Object... x) { LinkedHashMap map = new LinkedHashMap(); litmap_impl(map, x); return map; } static Object callF(Object f, Object... args) { return callFunction(f, args); } static String unnull(String s) { return s == null ? "" : s; } static List unnull(List l) { return l == null ? emptyList() : l; } static Iterable unnull(Iterable i) { return i == null ? emptyList() : i; } static Object[] unnull(Object[] a) { return a == null ? new Object[0] : a; } static List unclusterImages(BWImage img) { int w = img.getWidth(), h = img.getHeight(); List bc = unclusterImages_findBlackCols(img); List br = unclusterImages_findBlackRows(img); if (!bc.contains(0) || !br.contains(0) || !bc.contains(w-1) || !br.contains(h-1)) return null; List images = new ArrayList(); for (int y = 1; y < l(br)-1; y++) for (int x = 1; x < l(bc)-1; x++) { int x1 = bc.get(x)+1, cw = bc.get(x+1)-x1; int y1 = br.get(y)+1, ch = br.get(y+1)-y1; images.add(img.clip(x1, y1, cw, ch)); } return images; } static List unclusterImages_findBlackCols(BWImage img) { List l = new ArrayList(); int w = img.getWidth(), h = img.getHeight(); outer: for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) if (img.getPixel(x, y) > 0.1f) continue outer; l.add(x); } return l; } static List unclusterImages_findBlackRows(BWImage img) { List l = new ArrayList(); int w = img.getWidth(), h = img.getHeight(); outer: for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) if (img.getPixel(x, y) > 0.1f) continue outer; l.add(y); } return l; } static String substring(String s, int x) { return safeSubstring(s, x); } static String substring(String s, int x, int y) { return safeSubstring(s, x, y); } static String getStackTrace(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return writer.toString(); } static Pair unsubtitle(BWImage img) { int h = img.getHeight(), y1 = h-12; if (y1 < 0) return null; int w = img.getWidth(); int x1 = w, x2 = 0; for (int x = 0; x < w; x++) if (img.getPixel(x, y1) < 0.5) { x1 = min(x1, x); x2 = max(x2, x+1); if (img.getPixel(x, h-1) >= 0.5) return null; } else if (img.getPixel(x, h-1) < 0.5) return null; if (x1 > x2) return null; BWImage sub = img.clip(x1-1, y1, x2-x1+2, h-y1); //showBWImage(sub); String text = imageToUtf(sub); return pair(img.clip(0, 0, w, y1), text); } static String baseClassName(String className) { return substring(className, className.lastIndexOf('.')); } static String baseClassName(Object o) { return baseClassName(getClassName(o)); } static void readLocally(String progID, String varNames) { readLocally2(mc(), progID, varNames); } static void readLocally(String varNames) { readLocally2(mc(), programID(), varNames); } static void readLocally2(Object obj, String varNames) { readLocally2(obj, programID(), varNames); } // read a string variable from standard storage // does not overwrite variable contents if there is no file static synchronized void readLocally2(Object obj, String progID, String varNames) { for (String variableName : codeTokensOnly(javaTok(varNames))) { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); String value = loadTextFile(textFile); if (value != null) set(main.class, variableName, value); else { value = loadTextFile(structureFile); if (value != null) readLocally_set(obj, variableName, unstructure(value)); } } } static void readLocally_set(Object c, String varName, Object value) { Object oldValue = get(c, varName); if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) { // Assume it's a synchroList. value = synchroList((List) value); } set(c, varName, value); } static List map(Iterable l, Object f) { return map(f, l); } static List map(Object f, Iterable l) { List x = new ArrayList(); Object mc = mc(); for (Object o : unnull(l)) x.add(callFunction(f, o)); return x; } static List map(Object f, Object[] l) { return map(f, asList(l)); } static BWImage removeGrayBorder(BWImage image) { float threshold = 0.9f; int w = image.getWidth(), h = image.getHeight(); int x1 = w, x2 = 0, y1 = h, y2 = 0; // haha, slow algorithm :) for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { float f = image.getPixel(x, y); if (f < threshold || f == 1) { x1 = Math.min(x1, x); x2 = Math.max(x2, x); y1 = Math.min(y1, y); y2 = Math.max(y2, y); } } if (x1 == 0 && y1 == 0 && x2 == w-1 && y2 == h-1) return image; else if (x2 < x1 || y2 < y1) return null; else return image.clip(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } static BWImage loadBWImage(String snippetIDOrURL) { return new BWImage(loadBufferedImage(snippetIDOrURL)); } static void saveLocally(String variableName) { saveLocally(programID(), variableName); } static void saveLocally(String progID, String variableName) { saveLocally2(mc(), progID, variableName); } static void saveLocally2(Object obj, String variableName) { saveLocally2(obj, programID(), variableName); } static synchronized void saveLocally2(Object obj, String progID, String variableName) { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); Object x = get(obj, variableName); if (x == null) { textFile.delete(); structureFile.delete(); } else if (x instanceof String) { saveTextFile(textFile, (String) x); structureFile.delete(); } else { saveTextFile(structureFile, structure(x)); textFile.delete(); } } static Throwable getInnerException(Throwable e) { while (e.getCause() != null) e = e.getCause(); return e; } static String structure(Object o) { HashSet refd = new HashSet(); return structure_2(structure_1(o, 0, new IdentityHashMap(), refd), refd); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static String structure_1(Object o, int stringSizeLimit, IdentityHashMap seen, HashSet refd) { if (o == null) return "null"; // these are never back-referenced (for readability) if (o instanceof String) return quote(stringSizeLimit != 0 ? shorten((String) o, stringSizeLimit) : (String) o); if (o instanceof BigInteger) return "bigint(" + o + ")"; if (o instanceof Double) return "d(" + quote(str(o)) + ")"; if (o instanceof Float) return "fl " + quote(str(o)); if (o instanceof Long) return o + "L"; if (o instanceof Integer) return str(o); if (o instanceof Boolean) return ((Boolean) o).booleanValue() ? "t" : "f"; if (o instanceof Character) return quoteCharacter((Character) o); if (o instanceof File) return "File " + quote(((File) o).getPath()); // referencable objects follow Integer ref = seen.get(o); if (ref != null) { refd.add(ref); return "r" + ref; } ref = seen.size()+1; seen.put(o, ref); String r = "m" + ref + " "; // marker String name = o.getClass().getName(); StringBuilder buf = new StringBuilder(); if (o instanceof HashSet) return r + "hashset " + structure_1(new ArrayList((Set) o), stringSizeLimit, seen, refd); if (o instanceof TreeSet) return r + "treeset " + structure_1(new ArrayList((Set) o), stringSizeLimit, seen, refd); if (o instanceof Collection) { for (Object x : (Collection) o) { if (buf.length() != 0) buf.append(", "); buf.append(structure_1(x, stringSizeLimit, seen, refd)); } return r + "[" + buf + "]"; } if (o instanceof Map) { for (Object e : ((Map) o).entrySet()) { if (buf.length() != 0) buf.append(", "); buf.append(structure_1(((Map.Entry) e).getKey(), stringSizeLimit, seen, refd)); buf.append("="); buf.append(structure_1(((Map.Entry) e).getValue(), stringSizeLimit, seen, refd)); } return r + (o instanceof HashMap ? "hashmap" : "") + "{" + buf + "}"; } if (o.getClass().isArray()) { if (o instanceof byte[]) return "ba " + quote(bytesToHex((byte[]) o)); int n = Array.getLength(o); for (int i = 0; i < n; i++) { if (buf.length() != 0) buf.append(", "); buf.append(structure_1(Array.get(o, i), stringSizeLimit, seen, refd)); } return r + "array{" + buf + "}"; } if (o instanceof Class) return r + "class(" + quote(((Class) o).getName()) + ")"; if (o instanceof Throwable) return r + "exception(" + quote(((Throwable) o).getMessage()) + ")"; if (o instanceof BitSet) { BitSet bs = (BitSet) o; for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) { if (buf.length() != 0) buf.append(", "); buf.append(i); } return "bitset{" + buf + "}"; } // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) return r + String.valueOf(o); String shortName = o.getClass().getName().replaceAll("^main\\$", ""); if (shortName.equals("Lisp")) { buf.append("l(" + structure_1(getOpt(o, "head"), stringSizeLimit, seen, refd)); List args = (List) ( getOpt(o, "args")); if (nempty(args)) for (int i = 0; i < l(args); i++) { buf.append(", "); Object arg = args.get(i); // sweet shortening if (arg != null && eq(arg.getClass().getName(), "main$Lisp") && isTrue(call(arg, "isEmpty"))) arg = get(arg, "head"); buf.append(structure_1(arg, stringSizeLimit, seen, refd)); } buf.append(")"); return r + str(buf); } int numFields = 0; String fieldName = ""; if (shortName.equals("DynamicObject")) { shortName = (String) get_raw(o, "className"); Map fieldValues = (Map) get_raw(o, "fieldValues"); for (String _fieldName : fieldValues.keySet()) { fieldName = _fieldName; Object value = fieldValues.get(fieldName); if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure_1(value, stringSizeLimit, seen, refd)); } ++numFields; } } else { // regular class Class c = o.getClass(); while (c != Object.class) { Field[] fields = c.getDeclaredFields(); for (Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; fieldName = field.getName(); // skip outer object reference if (fieldName.indexOf("$") >= 0) continue; Object value; try { field.setAccessible(true); value = field.get(o); } catch (Exception e) { value = "?"; } // put special cases here... if (value != null) { if (buf.length() != 0) buf.append(", "); buf.append(fieldName + "=" + structure_1(value, stringSizeLimit, seen, refd)); } ++numFields; } c = c.getSuperclass(); } } String b = buf.toString(); if (numFields == 1 && structure_allowShortening) b = b.replaceAll("^" + fieldName + "=", ""); // drop field name if only one String s = shortName; if (buf.length() != 0) s += "(" + b + ")"; return r + s; } // drop unused markers static String structure_2(String s, HashSet refd) { List tok = javaTok(s); StringBuilder out = new StringBuilder(); for (int i = 1; i < l(tok); i += 2) { String t = tok.get(i); if (t.startsWith("m") && isInteger(t.substring(1)) && !refd.contains(parseInt(t.substring(1)))) continue; out.append(t).append(tok.get(i+1)); } return str(out); } static String str(Object o) { return String.valueOf(o); } static int hexToInt(String s) { return Integer.parseInt(s, 16); } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } static RuntimeException fail(String msg) { throw new RuntimeException(unnull(msg)); } // disabled for now to shorten some programs /*static RuntimeException fail(S msg, O... args) { throw new RuntimeException(format(msg, args)); }*/ static Map similarEmptyMap(Map m) { if (m instanceof TreeMap) return new TreeMap(); if (m instanceof LinkedHashMap) return new LinkedHashMap(); // default to a hash map return new HashMap(); } static Object callFunction(Object f, Object... args) { if (f == null) return null; if (f instanceof Runnable) { ((Runnable) f).run(); return null; } else if (f instanceof String) return call(mc(), (String) f, args); else return call(f, "get", args); //else throw fail("Can't call a " + getClassName(f)); } static ArrayList asList(A[] a) { return new ArrayList(Arrays.asList(a)); } static ArrayList asList(int[] a) { ArrayList l = new ArrayList(); for (int i : a) l.add(i); return l; } static ArrayList asList(Iterable s) { if (s instanceof ArrayList) return (ArrayList) s; ArrayList l = new ArrayList(); if (s != null) for (A a : s) l.add(a); return l; } // hopefully covers all cases :) static String safeSubstring(String s, int x, int y) { if (s == null) return null; if (x < 0) x = 0; if (x > s.length()) return ""; if (y < x) y = x; if (y > s.length()) y = s.length(); return s.substring(x, y); } static String safeSubstring(String s, int x) { return safeSubstring(s, x, l(s)); } public static String bytesToHex(byte[] bytes) { return bytesToHex(bytes, 0, bytes.length); } public static String bytesToHex(byte[] bytes, int ofs, int len) { StringBuilder stringBuilder = new StringBuilder(len*2); for (int i = 0; i < len; i++) { String s = "0" + Integer.toHexString(bytes[ofs+i]); stringBuilder.append(s.substring(s.length()-2, s.length())); } return stringBuilder.toString(); } static boolean isInteger(String s) { return s != null && Pattern.matches("\\-?\\d+", s); } static String quote(String s) { if (s == null) return "null"; return "\"" + s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n") + "\""; } static String quote(long l) { return quote("" + l); } static String quote(char c) { return quote("" + c); } static String shorten(String s, int max) { if (s == null) return ""; return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "..."; } static Class mc() { return getMainClass(); } static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { return new File(javaxDataDir(), formatSnippetID(snippetID)); } static String quoteCharacter(char c) { if (c == '\'') return "'\\''"; if (c == '\\') return "'\\\\'"; return "'" + c + "'"; } static List codeTokensOnly(List tok) { List l = new ArrayList(); for (int i = 1; i < tok.size(); i += 2) l.add(tok.get(i)); return l; } // get purpose 1: access a list/array (safer version of x.get(y)) static A get(List l, int idx) { return idx >= 0 && idx < l(l) ? l.get(idx) : null; } static A get(A[] l, int idx) { return idx >= 0 && idx < l(l) ? l[idx] : null; } // get purpose 2: access a field by reflection or a map static Object get(Object o, String field) { if (o instanceof Class) return get((Class) o, field); if (o instanceof Map) return ((Map) o).get(field); if (o.getClass().getName().equals("main$DynamicObject")) return call(get_raw(o, "fieldValues"), "get", field); return get_raw(o, field); } static Object get_raw(Object o, String field) { try { Field f = get_findField(o.getClass(), field); f.setAccessible(true); return f.get(o); } catch (Exception e) { throw new RuntimeException(e); } } static Object get(Class c, String field) { try { Field f = get_findStaticField(c, field); f.setAccessible(true); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field get_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field get_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } /** writes safely (to temp file, then rename) */ public static void saveTextFile(String fileName, String contents) throws IOException { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; File tempFile = new File(tempFileName); if (contents != null) { if (tempFile.exists()) try { String saveName = tempFileName + ".saved." + now(); copyFile(tempFile, new File(saveName)); } catch (Throwable e) { printStackTrace(e); } FileOutputStream fileOutputStream = new FileOutputStream(tempFile.getPath()); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); printWriter.print(contents); printWriter.close(); } if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (contents != null) if (!tempFile.renameTo(file)) throw new IOException("Can't rename " + tempFile + " to " + file); } public static void saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } static String getClassName(Object o) { return o == null ? "null" : o.getClass().getName(); } // replacement for class JavaTok // maybe incomplete, might want to add floating point numbers // todo also: extended multi-line strings static int javaTok_n, javaTok_elements; static boolean javaTok_opt; static List javaTok(String s) { return javaTok(s, null); } static List javaTok(String s, List existing) { ++javaTok_n; int nExisting = javaTok_opt && existing != null ? existing.size() : 0; List tok = existing != null ? new ArrayList(nExisting) : new ArrayList(); int l = s.length(); int i = 0, n = 0; while (i < l) { int j = i; char c; String cc; // scan for whitespace while (j < l) { c = s.charAt(j); cc = s.substring(j, Math.min(j+2, l)); if (c == ' ' || c == '\t' || c == '\r' || c == '\n') ++j; else if (cc.equals("/*")) { do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/")); j = Math.min(j+2, l); } else if (cc.equals("//")) { do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0); } else break; } if (n < nExisting && javaTok_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(quickSubstring(s, i, j)); ++n; i = j; if (i >= l) break; c = s.charAt(i); // cc is not needed in rest of loop body cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { if (s.charAt(j) == opener || s.charAt(j) == '\n') { // end at \n to not propagate unclosed string literal errors ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || "'".indexOf(s.charAt(j)) >= 0)); // for stuff like "don't" else if (Character.isDigit(c)) { do ++j; while (j < l && Character.isDigit(s.charAt(j))); if (j < l && s.charAt(j) == 'L') ++j; // Long constants like 1L } else if (cc.equals("[[")) { do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]")); j = Math.min(j+2, l); } else if (cc.equals("[=") && i+2 < l && s.charAt(i+2) == '[') { do ++j; while (j+2 < l && !s.substring(j, j+3).equals("]=]")); j = Math.min(j+3, l); } else ++j; if (n < nExisting && javaTok_isCopyable(existing.get(n), s, i, j)) tok.add(existing.get(n)); else tok.add(quickSubstring(s, i, j)); ++n; i = j; } if ((tok.size() % 2) == 0) tok.add(""); javaTok_elements += tok.size(); return tok; } static List javaTok(List tok) { return javaTok(join(tok), tok); } static boolean javaTok_isCopyable(String t, String s, int i, int j) { return t.length() == j-i && s.regionMatches(i, t, 0, j-i); // << could be left out, but that's brave } static Object unstructure(String text) { return unstructure(text, false); } static Object unstructure(String text, final boolean allDynamic) { return unstructure(text, allDynamic, null); } // TODO: backrefs for hashmap{} etc // classFinder: name -> class static Object unstructure(String text, final boolean allDynamic, final Object classFinder) { if (text == null) return null; final List tok = javaTok(text); final boolean debug = unstructure_debug; class X { int i = 1; HashMap refs = new HashMap(); Object parse() { String t = tok.get(i); int refID = 0; if (t.startsWith("m") && isInteger(t.substring(1))) { refID = parseInt(t.substring(1)); i += 2; t = tok.get(i); } if (debug) print("parse: " + quote(t)); if (t.startsWith("\"")) { String s = unquote(tok.get(i)); i += 2; return s; } if (t.startsWith("'")) { char c = unquoteCharacter(tok.get(i)); i += 2; return c; } if (t.equals("bigint")) return parseBigInt(); if (t.equals("d")) return parseDouble(); if (t.equals("fl")) return parseFloat(); if (t.equals("false") || t.equals("f")) { i += 2; return false; } if (t.equals("true") || t.equals("t")) { i += 2; return true; } if (t.equals("-")) { t = tok.get(i+2); i += 4; return isLongConstant(t) ? (Object) (-parseLong(t)) : (Object) (-parseInt(t)); } if (isInteger(t) || isLongConstant(t)) { i += 2; if (debug) print("isLongConstant " + quote(t) + " => " + isLongConstant(t)); if (isLongConstant(t)) return parseLong(t); long l = parseLong(t); return l != (int) l ? new Long(l) : new Integer((int) l); } if (t.equals("File")) { File f = new File(unquote(tok.get(i+2))); i += 4; return f; } if (t.startsWith("r") && isInteger(t.substring(1))) { i += 2; int ref = Integer.parseInt(t.substring(1)); Object o = refs.get(ref); if (o == null) print("Warning: unsatisfied back reference " + ref); return o; } return parse_inner(refID); } // everything that can be backreferenced Object parse_inner(int refID) { String t = tok.get(i); if (debug) print("parse_inner: " + quote(t)); if (t.equals("hashset")) return parseHashSet(); if (t.equals("treeset")) return parseTreeSet(); if (t.equals("hashmap")) return parseHashMap(); if (t.equals("{")) return parseMap(); if (t.equals("[")) return parseList(); if (t.equals("array")) return parseArray(); if (t.equals("ba")) { String hex = unquote(tok.get(i+2)); i += 4; return hexToBytes(hex); } if (t.equals("class")) return parseClass(); if (t.equals("l")) return parseLisp(); if (t.equals("null")) { i += 2; return null; } /* in dev. if (!allDynamic && t.equals("run")) { S snippetID = unquote(t.get(i+2)); i += 4; run( } */ // any other class name if (isJavaIdentifier(t)) { Class c; if (allDynamic) c = null; else if (classFinder != null) c = (Class) callF(classFinder, t); else c = findClass(t); DynamicObject dO = null; Object o = null; if (c != null) o = nuObject(c); else { dO = new DynamicObject(); dO.className = t; } if (refID != 0) refs.put(refID, o); i += 2; if (i < tok.size() && tok.get(i).equals("(")) { consume("("); while (!tok.get(i).equals(")")) { // It's like parsing a map. //Object key = parse(); //if (tok.get(i).equals(")")) // key = onlyField(); String key = unquote(tok.get(i)); i += 2; consume("="); Object value = parse(); if (o != null) setOpt(o, key, value); else dO.fieldValues.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume(")"); } return o != null ? o : dO; } throw new RuntimeException("Unknown token " + (i+1) + ": " + t); } Object parseSet(Set set) { set.addAll((List) parseList()); return set; } Object parseLisp() { consume("l"); consume("("); List list = new ArrayList(); while (!tok.get(i).equals(")")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume(")"); return newObject("main$Lisp", (String) list.get(0), subList(list, 1)); } Object parseList() { consume("["); List list = new ArrayList(); while (!tok.get(i).equals("]")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("]"); return list; } Object parseArray() { consume("array"); consume("{"); List list = new ArrayList(); while (!tok.get(i).equals("}")) { list.add(parse()); if (tok.get(i).equals(",")) i += 2; } consume("}"); return list.toArray(); } Object parseClass() { consume("class"); consume("("); String name = tok.get(i); i += 2; consume(")"); Class c = allDynamic ? null : findClass(name); if (c != null) return c; DynamicObject dO = new DynamicObject(); dO.className = "java.lang.Class"; dO.fieldValues.put("name", name); return dO; } Object parseBigInt() { consume("bigint"); consume("("); String val = tok.get(i); i += 2; if (eq(val, "-")) { val = "-" + tok.get(i); i += 2; } consume(")"); return new BigInteger(val); } Object parseDouble() { consume("d"); consume("("); String val = unquote(tok.get(i)); i += 2; consume(")"); return Double.parseDouble(val); } Object parseFloat() { consume("fl"); String val; if (eq(tok.get(i), "(")) { consume("("); val = unquote(tok.get(i)); i += 2; consume(")"); } else { val = unquote(tok.get(i)); i += 2; } return Float.parseFloat(val); } Object parseHashMap() { consume("hashmap"); return parseMap(new HashMap()); } Object parseHashSet() { consume("hashset"); return parseSet(new HashSet()); } Object parseTreeSet() { consume("treeset"); return parseSet(new TreeSet()); } Object parseMap() { return parseMap(new TreeMap()); } Object parseMap(Map map) { consume("{"); while (!tok.get(i).equals("}")) { Object key = parse(); consume("="); Object value = parse(); map.put(key, value); if (tok.get(i).equals(",")) i += 2; } consume("}"); return map; } void consume(String s) { if (!tok.get(i).equals(s)) { String prevToken = i-2 >= 0 ? tok.get(i-2) : ""; String nextTokens = join(tok.subList(i, Math.min(i+4, tok.size()))); throw fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new X().parse(); } static boolean unstructure_debug; static List emptyList() { return new ArrayList(); //ret Collections.emptyList(); } static int min(int a, int b) { return Math.min(a, b); } static double min(double[] c) { double x = Double.MAX_VALUE; for (double d : c) x = Math.min(x, d); return x; } static byte min(byte[] c) { byte x = 127; for (byte d : c) if (d < x) x = d; return x; } static List synchroList() { return Collections.synchronizedList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static String programID() { return getProgramID(); } public static String loadTextFile(String fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(String fileName, String defaultContents) throws IOException { if (!new File(fileName).exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(fileName); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); return loadTextFile(inputStreamReader); } public static String loadTextFile(File fileName) { try { return loadTextFile(fileName, null); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(File fileName, String defaultContents) throws IOException { try { return loadTextFile(fileName.getPath(), defaultContents); } catch (IOException e) { throw new RuntimeException(e); } } public static String loadTextFile(Reader reader) throws IOException { StringBuilder builder = new StringBuilder(); try { char[] buffer = new char[1024]; int n; while (-1 != (n = reader.read(buffer))) builder.append(buffer, 0, n); } finally { reader.close(); } return builder.toString(); } static void set(Object o, String field, Object value) { if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void set(Class c, String field, Object value) { try { Field f = set_findStaticField(c, field); smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field set_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field set_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static boolean nempty(Collection c) { return !isEmpty(c); } static boolean nempty(CharSequence s) { return !isEmpty(s); } static boolean nempty(Object[] o) { return !isEmpty(o); } static boolean nempty(Map m) { return !isEmpty(m); } static boolean nempty(Iterator i) { return i != null && i.hasNext(); } static Pair pair(A a, B b) { return new Pair(a, b); } static Object call(Object o) { return callFunction(o); } // varargs assignment fixer for a single string array argument static Object call(Object o, String method, String[] arg) { return call(o, method, new Object[] {arg}); } static Object call(Object o, String method, Object... args) { try { if (o instanceof Class) { Method m = call_findStaticMethod((Class) o, method, args, false); m.setAccessible(true); return m.invoke(null, args); } else { Method m = call_findMethod(o, method, args, false); m.setAccessible(true); return m.invoke(o, args); } } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } } static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (!m.getName().equals(method)) { if (debug) System.out.println("Method name mismatch: " + method); continue; } if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug)) continue; return m; } c = c.getSuperclass(); } throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + _c.getName()); } static Method call_findMethod(Object o, String method, Object[] args, boolean debug) { Class c = o.getClass(); while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (m.getName().equals(method) && call_checkArgs(m, args, debug)) return m; } c = c.getSuperclass(); } throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName()); } private static boolean call_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static int parseInt(String s) { return empty(s) ? 0 : Integer.parseInt(s); } // hmm, this shouldn't call functions really. That was just // for coroutines. static boolean isTrue(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); if (o == null) return false; return ((Boolean) callF(o)).booleanValue(); } static boolean isTrue(Object pred, Object arg) { return booleanValue(callF(pred, arg)); } static String imageToUtf(BWImage img) { int w = img.getWidth(), h = img.getHeight(); if (h < 12) return null; byte[] data = new byte[w-2]; for (int x = 1; x < w-1; x++) { int b = 0; if (img.getPixel(x, 0) != 0 || img.getPixel(x, 11) != 0) throw fail("not an utf8 image"); for (int y = 2; y < 2+8; y++) b = b << 1 | (img.getPixel(x, y) > .5f ? 0 : 1); data[x-1] = (byte) b; } return fromUtf8(data); } static Object getOpt(Object o, String field) { if (o instanceof String) o = getBot ((String) o); if (o == null) return null; if (o instanceof Class) return getOpt((Class) o, field); if (o.getClass().getName().equals("main$DynamicObject")) return ((Map) getOpt_raw(o, "fieldValues")).get(field); if (o instanceof Map) return ((Map) o).get(field); return getOpt_raw(o, field); } static Object getOpt_raw(Object o, String field) { try { Field f = getOpt_findField(o.getClass(), field); if (f == null) return null; f.setAccessible(true); return f.get(o); } catch (Exception e) { throw new RuntimeException(e); } } static Object getOpt(Class c, String field) { try { if (c == null) return null; Field f = getOpt_findStaticField(c, field); if (f == null) return null; f.setAccessible(true); return f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } static Field getOpt_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Field getOpt_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static boolean loadBufferedImage_useImageCache = true; static BufferedImage loadBufferedImage(String snippetIDOrURL) { try { if (isURL(snippetIDOrURL)) return ImageIO.read(new URL(snippetIDOrURL)); if (!isSnippetID(snippetIDOrURL)) throw fail("Not a URL or snippet ID: " + snippetIDOrURL); String snippetID = "" + parseSnippetID(snippetIDOrURL); try { // TODO: androidify File dir = new File(System.getProperty("user.home"), ".tinybrain/image-cache"); if (loadBufferedImage_useImageCache) { dir.mkdirs(); File file = new File(dir, snippetID + ".png"); if (file.exists() && file.length() != 0) try { return ImageIO.read(file); } catch (Throwable e) { e.printStackTrace(); // fall back to loading from sourceforge } } String imageURL = snippetImageURL(snippetID); System.err.println("Loading image: " + imageURL); BufferedImage image = ImageIO.read(new URL(imageURL)); if (loadBufferedImage_useImageCache) { File tempFile = new File(dir, snippetID + ".tmp." + System.currentTimeMillis()); ImageIO.write(image, "png", tempFile); tempFile.renameTo(new File(dir, snippetID + ".png")); //Log.info("Cached image."); } //Log.info("Loaded image."); return image; } catch (IOException e) { throw new RuntimeException(e); } } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static BufferedImage loadBufferedImage(File file) { try { return ImageIO.read(file); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Map litmap(Object... x) { TreeMap map = new TreeMap(); litmap_impl(map, x); return map; } static void litmap_impl(Map map, Object... x) { for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); } static boolean isURL(String s) { return s.startsWith("http://") || s.startsWith("https://"); } static Object getBot(String botID) { return callOpt(getMainBot(), "getBot", botID); } public static String join(String glue, Iterable strings) { StringBuilder buf = new StringBuilder(); Iterator i = strings.iterator(); if (i.hasNext()) { buf.append(i.next()); while (i.hasNext()) buf.append(glue).append(i.next()); } return buf.toString(); } public static String join(String glue, String[] strings) { return join(glue, Arrays.asList(strings)); } public static String join(Iterable strings) { return join("", strings); } public static String join(String[] strings) { return join("", strings); } static byte[] hexToBytes(String s) { int n = l(s) / 2; byte[] bytes = new byte[n]; for (int i = 0; i < n; i++) bytes[i] = (byte) parseHexByte(substring(s, i*2, i*2+2)); return bytes; } // currently finds only inner classes of class "main" // returns null on not found // this is the simple version that is not case-tolerant static Class findClass(String name) { try { return Class.forName("main$" + name); } catch (ClassNotFoundException e) { return null; } } static String quickSubstring(String s, int i, int j) { if (i == j) return ""; return s.substring(i, j); } static boolean isJavaIdentifier(String s) { if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i < s.length(); i++) if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; return true; } public static void copyFile(File src, File dest) throws IOException { mkdirsForFile(dest); FileInputStream inputStream = new FileInputStream(src.getPath()); FileOutputStream outputStream = new FileOutputStream(dest.getPath()); try { copyStream(inputStream, outputStream); inputStream.close(); } finally { outputStream.close(); } } static String programID; static String getProgramID() { return nempty(programID) ? formatSnippetID(programID) : "?"; } // TODO: ask JavaX instead static String getProgramID(Class c) { String id = (String) getOpt(c, "programID"); if (nempty(id)) return formatSnippetID(id); return "?"; } static String getProgramID(Object o) { return getProgramID(getMainClass(o)); } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static Object newObject(Class c, Object... args) { return nuObject(c, args); } static Object newObject(String className, Object... args) { return nuObject(className, args); } static boolean isLongConstant(String s) { if (!s.endsWith("L")) return false; s = s.substring(0, l(s)-1); return isInteger(s); } static Class getMainClass() { try { return Class.forName("main"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Class getMainClass(Object o) { try { return (o instanceof Class ? (Class) o : o.getClass()).getClassLoader().loadClass("main"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static List subList(List l, int startIndex) { return subList(l, startIndex, l(l)); } static List subList(List l, int startIndex, int endIndex) { startIndex = max(0, min(l(l), startIndex)); endIndex = max(0, min(l(l), endIndex)); if (startIndex > endIndex) return litlist(); return l.subList(startIndex, endIndex); } static long parseLong(String s) { if (s == null) return 0; return Long.parseLong(dropSuffix("L", s)); } static long parseLong(Object s) { return Long.parseLong((String) s); } static boolean empty(Collection c) { return isEmpty(c); } static boolean empty(String s) { return isEmpty(s); } static boolean empty(Map map) { return map == null || map.isEmpty(); } static boolean empty(Object[] o) { return o == null || o.length == 0; } static boolean empty(Object o) { if (o instanceof Collection) return empty((Collection) o); if (o instanceof String) return empty((String) o); if (o instanceof Map) return empty((Map) o); if (o instanceof Object[]) return empty((Object[]) o); throw fail("unknown type for 'empty': " + getType(o)); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } // extended over Class.isInstance() to handle primitive types static boolean isInstanceX(Class type, Object arg) { if (type == boolean.class) return arg instanceof Boolean; if (type == int.class) return arg instanceof Integer; if (type == long.class) return arg instanceof Long; if (type == float.class) return arg instanceof Float; if (type == short.class) return arg instanceof Short; if (type == char.class) return arg instanceof Character; if (type == byte.class) return arg instanceof Byte; if (type == double.class) return arg instanceof Double; return type.isInstance(arg); } static String snippetImageURL(String snippetID) { long id = parseSnippetID(snippetID); String url; if (id == 1000010 || id == 1000012) url = "http://tinybrain.de:8080/tb/show-blobimage.php?id=" + id; else url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + id + "&contentType=image/png"; return url; } static Object nuObject(String className, Object... args) { try { return nuObject(Class.forName(className), args); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Object nuObject(Object realm, String className, Object... args) { return nuObject(_getClass(realm, className), args); } static A nuObject(Class c, Object... args) { try { Constructor m = nuObject_findConstructor(c, args); m.setAccessible(true); return (A) m.newInstance(args); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static Constructor nuObject_findConstructor(Class c, Object... args) { for (Constructor m : c.getDeclaredConstructors()) { if (!nuObject_checkArgs(m.getParameterTypes(), args, false)) continue; return m; } throw new RuntimeException("Constructor with " + args.length + " matching parameter(s) not found in " + c.getName()); } static boolean nuObject_checkArgs(Class[] types, Object[] args, boolean debug) { if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static boolean booleanValue(Object o) { return eq(true, o); } static File javaxDataDir_dir; // can be set to work on different base dir static File javaxDataDir() { return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data"); } static double parseDouble(String s) { return Double.parseDouble(s); } public static long parseSnippetID(String snippetID) { long id = Long.parseLong(shortenSnippetID(snippetID)); if (id == 0) throw fail("0 is not a snippet ID"); return id; } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } public static String unquote(String s) { if (s == null) return null; if (s.startsWith("[")) { int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; if (i < s.length() && s.charAt(i) == '[') { String m = s.substring(1, i); if (s.endsWith("]" + m + "]")) return s.substring(i+1, s.length()-i-1); } } if (s.startsWith("\"") /*&& s.endsWith("\"")*/ && s.length() > 1) { String st = s.substring(1, s.endsWith("\"") ? s.length()-1 : s.length()); StringBuilder sb = new StringBuilder(st.length()); for (int i = 0; i < st.length(); i++) { char ch = st.charAt(i); if (ch == '\\') { char nextChar = (i == st.length() - 1) ? '\\' : st .charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) <= '7') { code += st.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { case '\\': ch = '\\'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case '\"': ch = '\"'; break; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= st.length() - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + st.charAt(i + 2) + st.charAt(i + 3) + st.charAt(i + 4) + st.charAt(i + 5), 16); sb.append(Character.toChars(code)); i += 5; continue; default: ch = nextChar; // added by Stefan } i++; } sb.append(ch); } return sb.toString(); } else return s; // return original } static boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(CharSequence s) { return s == null || s.length() == 0; } static boolean isEmpty(Object[] a) { return a == null || a.length == 0; } static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } static void smartSet(Field f, Object o, Object value) throws Exception { f.setAccessible(true); // take care of common case (long to int) if (f.getType() == int.class && value instanceof Long) value = ((Long) value).intValue(); f.set(o, value); } static String fromUtf8(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static char unquoteCharacter(String s) { assertTrue(s.startsWith("'") && s.length() > 1); return unquote("\"" + s.substring(1, s.endsWith("'") ? s.length()-1 : s.length()) + "\"").charAt(0); } static void setOpt(Object o, String field, Object value) { if (o instanceof Class) setOpt((Class) o, field, value); else try { Field f = setOpt_findField(o.getClass(), field); if (f != null) smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } } static void setOpt(Class c, String field, Object value) { try { Field f = setOpt_findStaticField(c, field); if (f != null) smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field setOpt_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Field setOpt_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static String shortenSnippetID(String snippetID) { if (snippetID.startsWith("#")) snippetID = snippetID.substring(1); String httpBlaBla = "http://tinybrain.de/"; if (snippetID.startsWith(httpBlaBla)) snippetID = snippetID.substring(httpBlaBla.length()); return "" + parseLong(snippetID); } static String getType(Object o) { return getClassName(o); } static Object mainBot; static Object getMainBot() { return mainBot; } static String _userHome; static String userHome() { if (_userHome == null) { if (isAndroid()) _userHome = "/storage/sdcard0/"; else _userHome = System.getProperty("user.home"); //System.out.println("userHome: " + _userHome); } return _userHome; } static File userHome(String path) { return new File(userDir(), path); } static void assertTrue(Object o) { assertEquals(true, o); } static boolean assertTrue(String msg, boolean b) { if (!b) throw fail(msg); return b; } static boolean assertTrue(boolean b) { if (!b) throw fail("oops"); return b; } static ArrayList litlist(A... a) { return new ArrayList(Arrays.asList(a)); } static Class _getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; } } static Class _getClass(Object o) { return o instanceof Class ? (Class) o : o.getClass(); } static Class _getClass(Object realm, String name) { try { return getClass(realm).getClassLoader().loadClass(classNameToVM(name)); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} public static File mkdirsForFile(File file) { File dir = file.getParentFile(); if (dir != null) // is null if file is in current dir dir.mkdirs(); return file; } static Object callOpt(Object o) { if (o == null) return null; return callF(o); } static Object callOpt(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Method m = callOpt_findStaticMethod((Class) o, method, args, false); if (m == null) return null; m.setAccessible(true); return m.invoke(null, args); } else { Method m = callOpt_findMethod(o, method, args, false); if (m == null) return null; m.setAccessible(true); return m.invoke(o, args); } } catch (Exception e) { throw new RuntimeException(e); } } static Method callOpt_findStaticMethod(Class c, String method, Object[] args, boolean debug) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (!m.getName().equals(method)) { if (debug) System.out.println("Method name mismatch: " + method); continue; } if ((m.getModifiers() & Modifier.STATIC) == 0 || !callOpt_checkArgs(m, args, debug)) continue; return m; } c = c.getSuperclass(); } return null; } static Method callOpt_findMethod(Object o, String method, Object[] args, boolean debug) { Class c = o.getClass(); while (c != null) { for (Method m : c.getDeclaredMethods()) { if (debug) System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");; if (m.getName().equals(method) && callOpt_checkArgs(m, args, debug)) return m; } c = c.getSuperclass(); } return null; } private static boolean callOpt_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static String dropSuffix(String suffix, String s) { return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static int parseHexByte(String s) { return Integer.parseInt(s, 16); } public static void copyStream(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[65536]; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); } } static A assertEquals(Object x, A y) { return assertEquals(null, x, y); } static A assertEquals(String msg, Object x, A y) { if (!(x == null ? y == null : x.equals(y))) throw fail((msg != null ? msg + ": " : "") + structure(x) + " != " + structure(y)); return y; } static Class getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; } } static Class getClass(Object o) { return o instanceof Class ? (Class) o : o.getClass(); } static Class getClass(Object realm, String name) { try { try { return getClass(realm).getClassLoader().loadClass(classNameToVM(name)); } catch (ClassNotFoundException e) { return null; } } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} static String classNameToVM(String name) { return name.replace(".", "$"); } static File userDir() { return new File(userHome()); } static File userDir(String path) { return new File(userHome(), path); } static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; } static class Highest { A a; double score; boolean update(A x, double s) { if (a == null || s > score) { a = x; score = s; return true; } return false; } Pair getPair() { return a == null ? null : pair(a, score); } } static class DynamicObject { String className; // just the name, without the "main$" Map fieldValues = new TreeMap(); DynamicObject() {} // className = just the name, without the "main$" DynamicObject(String className) { this.className = className;} void put(String field, Object value) { fieldValues.put(field, value); } Object get(String field) { return fieldValues.get(field); } } static class Pair { A a; B b; Pair() {} Pair(A a, B b) { this.b = b; this.a = a;} public int hashCode() { return main.hashCode(a) + 2*main.hashCode(b); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Pair)) return false; Pair t = (Pair) o; return eq(a, t.a) && eq(b, t.b); } } static class BWImage { private int width, height; private BWImageSimpleStorage storage; // color returned when getPixel is called with a position outside the actual image private float borderColor = 0.0f; // for unstructure() BWImage() {} // BLACK! [Inefficient...] public BWImage(int width, int height) { this.width = width; this.height = height; byte[] bytePixels = new byte[width*height]; storage = makeStorage(width, height, bytePixels); } public BWImage(int width, int height, float[] pixels) { this.width = width; this.height = height; byte[] bytePixels = new byte[pixels.length]; for (int i = 0; i < pixels.length; i++) bytePixels[i] = toByte(pixels[i]); storage = makeStorage(width, height, bytePixels); } private BWImageSimpleStorage makeStorage(int width, int height, byte[] bytePixels) { return new BWImageSimpleStorage(width, height, bytePixels); } public BWImage(int width, int height, byte[] pixels) { this.height = height; this.width = width; storage = makeStorage(width, height, pixels); } public BWImage(BWImage image) { width = image.getWidth(); height = image.getHeight(); byte[] bytePixels = new byte[width*height]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) bytePixels[y*width+x] = image.getByte(x, y); storage = makeStorage(width, height, bytePixels); } public BWImage(RGBImage image) { width = image.getWidth(); height = image.getHeight(); byte[] result = new byte[height*width]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { RGB rgb = image.getRGB(x, y); result[y*width+x] = BWImage.toByte(rgb.getBrightness()); } storage = makeStorage(width, height, result); } public BWImage(BufferedImage image) { this(new RGBImage(image)); } /* too slow! public BWImage(BufferedImage image) { int width = image.getWidth(), height = image.getHeight(); byte[] pixels = new byte[width*height]; int[] linePixels = new int[width]; for (int y = 0; y < height; y++) { PixelGrabber pixelGrabber = new PixelGrabber(image, 0, y, width, 1, linePixels, 0, width); try { if (!pixelGrabber.grabPixels()) throw new RuntimeException("Could not grab pixels"); } catch (InterruptedException e) { throw new RuntimeException(e); } for (int x = 0; x < width; x++) pixels[y*width+x] = toByte(new RGB(linePixels[x]).getBrightness()); } } */ public byte getByte(int x, int y) { return inRange(x, y) ? storage.getByte(x,y ) : toByte(borderColor); } public BWImage(int width, int height, float brightness) { this.width = width; this.height = height; byte[] pixels = new byte[width*height]; for (int i = 0; i < pixels.length; i++) pixels[i] = toByte(brightness); storage = makeStorage(width, height, pixels); } public double averageBrightness() { double sum = 0; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) sum += getPixel(x, y); return (sum/(double) (height*width)); } public float minimumBrightness() { float min = 1; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) min = Math.min(min, getPixel(x, y)); return min; } public float getPixel(int x, int y) { return inRange(x, y) ? toFloat(storage.getByte(x,y )) : borderColor; } public static byte toByte(float pixel) { return (byte) (pixel*255f); } public static float toFloat(byte pixel) { return (((int) pixel) & 255)/255f; } private boolean inRange(int x, int y) { return x >= 0 && x < width && y >= 0 && y < height; } public int getWidth() { return width; } public int getHeight() { return height; } public RGBImage toRGB() { RGB[] rgbs = new RGB[width*height]; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { float p = getPixel(x, y); rgbs[y*width+x] = new RGB(p, p, p); } return new RGBImage(width, height, rgbs); } public BWImage clip(int x, int y, int w, int h) { return clip(new Rectangle(x, y, w, h)); } private Rectangle fixClipRect(Rectangle r) { return r.intersection(new Rectangle(0, 0, width, height)); } /** this should be multithread-safe */ public BWImage clip(Rectangle r) { r = fixClipRect(r); byte[] newPixels = new byte[r.height*r.width]; for (int y = 0; y < r.height; y++) for (int x = 0; x < r.width; x++) newPixels[y*r.width+x] = getByte(r.x+x, r.y+y); return new BWImage(r.width, r.height, newPixels); } public void setPixel(int x, int y, float brightness) { storage.setByte(x, y, toByte(fixPixel(brightness))); } public void setByte(int x, int y, byte brightness) { storage.setByte(x, y, brightness); } private float fixPixel(float pixel) { return Math.max(0, Math.min(1, pixel)); } public float getBorderColor() { return borderColor; } public void setBorderColor(float borderColor) { this.borderColor = borderColor; } public boolean anyPixelBrighterThan(double threshold) { for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) if (getPixel(x, y) > threshold) return true; return false; } BufferedImage getBufferedImage() { //ret toRGB().getBufferedImage(); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { int b = ((int) getByte(x, y) & 0xFF); bufferedImage.setRGB(x, y, b*0x010101); } return bufferedImage; } byte[] getBytes() { return storage.pixels; } } static class BWImageSimpleStorage { int width, height; byte[] pixels; // for unstructure() BWImageSimpleStorage() {} public BWImageSimpleStorage(int width, int height, byte[] pixels) { this.width = width; this.height = height; this.pixels = pixels; } public void setByte(int x, int y, byte b) { pixels[y*width+x] = b; } public byte getByte(int x, int y) { return pixels[y*width+x]; } } static class RGB { public final float r, g, b; public RGB(float r, float g, float b) { this.r = r; this.g = g; this.b = b; } public RGB(double r, double g, double b) { this.r = (float) r; this.g = (float) g; this.b = (float) b; } public RGB(double brightness) { this.r = this.g = this.b = (float) brightness; } public RGB(Color color) { this.r = color.getRed()/255f; this.g = color.getGreen()/255f; this.b = color.getBlue()/255f; } public RGB(String hex) { r = Integer.parseInt(hex.substring(0, 2), 16)/255f; g = Integer.parseInt(hex.substring(2, 4), 16)/255f; b = Integer.parseInt(hex.substring(4, 6), 16)/255f; } public float getComponent(int i) { return i == 0 ? r : i == 1 ? g : b; } public Color getColor() { return new Color(r, g, b); } public static RGB newSafe(float r, float g, float b) { return new RGB(Math.max(0, Math.min(1, r)), Math.max(0, Math.min(1, g)), Math.max(0, Math.min(1, b))); } public int asInt() { return getColor().getRGB() & 0xFFFFFF; } public float getBrightness() { return (r+g+b)/3.0f; } public String getHexString() { return Integer.toHexString(asInt() | 0xFF000000).substring(2).toUpperCase(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof RGB)) return false; RGB rgb = (RGB) o; if (Float.compare(rgb.b, b) != 0) return false; if (Float.compare(rgb.g, g) != 0) return false; if (Float.compare(rgb.r, r) != 0) return false; return true; } @Override public int hashCode() { int result = (r != +0.0f ? Float.floatToIntBits(r) : 0); result = 31 * result + (g != +0.0f ? Float.floatToIntBits(g) : 0); result = 31 * result + (b != +0.0f ? Float.floatToIntBits(b) : 0); return result; } public boolean isBlack() { return r == 0f && g == 0f && b == 0f; } public boolean isWhite() { return r == 1f && g == 1f && b == 1f; } public String toString() { return getHexString(); } } static class RGBImage { private BufferedImage bufferedImage; private File file; private int width, height; private int[] pixels; // color returned when getPixel is called with out-of-bounds position private int background = 0xFFFFFF; public RGBImage(BufferedImage image) { this(image, null); } public RGBImage(BufferedImage image, File file) { this.file = file; bufferedImage = image; width = image.getWidth(); height = image.getHeight(); pixels = new int[width*height]; PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); try { if (!pixelGrabber.grabPixels()) throw new RuntimeException("Could not grab pixels"); cleanPixels(); // set upper byte to 0 } catch (InterruptedException e) { throw new RuntimeException(e); } } /** We assume it's a file name to load from */ public RGBImage(String file) throws IOException { this(new File(file)); } public RGBImage(Dimension size, Color color) { this(size.width, size.height, color); } public RGBImage(Dimension size, RGB color) { this(size.width, size.height, color); } private void cleanPixels() { for (int i = 0; i < pixels.length; i++) pixels[i] &= 0xFFFFFF; } public RGBImage(int width, int height, int[] pixels) { this.width = width; this.height = height; this.pixels = pixels; } public RGBImage(int w, int h, RGB[] pixels) { this.width = w; this.height = h; this.pixels = asInts(pixels); } public static int[] asInts(RGB[] pixels) { int[] ints = new int[pixels.length]; for (int i = 0; i < pixels.length; i++) ints[i] = pixels[i] == null ? 0 : pixels[i].getColor().getRGB(); return ints; } public RGBImage(int w, int h) { this(w, h, Color.black); } public RGBImage(int w, int h, RGB rgb) { this.width = w; this.height = h; this.pixels = new int[w*h]; int col = rgb.asInt(); if (col != 0) for (int i = 0; i < pixels.length; i++) pixels[i] = col; } public RGBImage(RGBImage image) { this(image.width, image.height, copyPixels(image.pixels)); } public RGBImage(int width, int height, Color color) { this(width, height, new RGB(color)); } public RGBImage(File file) throws IOException { this(javax.imageio.ImageIO.read(file)); } private static int[] copyPixels(int[] pixels) { int[] copy = new int[pixels.length]; System.arraycopy(pixels, 0, copy, 0, pixels.length); return copy; } public int getIntPixel(int x, int y) { if (inRange(x, y)) return pixels[y * width + x]; else return background; } public static RGB asRGB(int packed) { int r = (packed >> 16) & 0xFF; int g = (packed >> 8) & 0xFF; int b = packed & 0xFF; return new RGB(r / 255f, g / 255f, b / 255f); } public RGB getRGB(int x, int y) { if (inRange(x, y)) return asRGB(pixels[y * width + x]); else return new RGB(background); } /** alias of getRGB - I kept typing getPixel instead of getRGB all the time, so I finally created it */ public RGB getPixel(int x, int y) { return getRGB(x, y); } public int getWidth() { return width; } public int getHeight() { return height; } /** Attention: cached, i.e. does not change when image itself changes */ /** @NotNull */ public BufferedImage getBufferedImage() { if (bufferedImage == null) { bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //bufferedImage.setData(Raster.createRaster(new SampleModel())); for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) bufferedImage.setRGB(x, y, pixels[y*width+x]); } return bufferedImage; } public RGBImage clip(Rectangle r) { r = fixClipRect(r); int[] newPixels; try { newPixels = new int[r.width*r.height]; } catch (RuntimeException e) { System.out.println(r); throw e; } for (int y = 0; y < r.height; y++) { System.arraycopy(pixels, (y+r.y)*width+r.x, newPixels, y*r.width, r.width); } return new RGBImage(r.width, r.height, newPixels); } private Rectangle fixClipRect(Rectangle r) { r = r.intersection(new Rectangle(0, 0, width, height)); if (r.isEmpty()) r = new Rectangle(r.x, r.y, 0, 0); return r; } public File getFile() { return file; } /** can now also do GIF (not just JPEG) */ public static RGBImage load(String fileName) { return load(new File(fileName)); } /** can now also do GIF (not just JPEG) */ public static RGBImage load(File file) { try { BufferedImage bufferedImage = javax.imageio.ImageIO.read(file); return new RGBImage(bufferedImage); } catch (IOException e) { throw new RuntimeException(e); } } public int getInt(int x, int y) { return pixels[y * width + x]; } public void save(File file) throws IOException { String name = file.getName().toLowerCase(); String type; if (name.endsWith(".png")) type = "png"; else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) type = "jpeg"; else throw new IOException("Unknown image extension: " + name); javax.imageio.ImageIO.write(getBufferedImage(), type, file); } public static RGBImage dummyImage() { return new RGBImage(1, 1, new int[] {0xFFFFFF}); } public int[] getPixels() { return pixels; } public void setPixel(int x, int y, RGB rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb.asInt(); } public void setPixel(int x, int y, Color color) { setPixel(x, y, new RGB(color)); } public void setPixel(int x, int y, int rgb) { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y*width+x] = rgb; } public RGBImage copy() { return new RGBImage(this); } public boolean inRange(int x, int y) { return x >= 0 && y >= 0 && x < width && y < height; } public int getBackground() { return background; } public void setBackground(int background) { this.background = background; } public Dimension getSize() { return new Dimension(width, height); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RGBImage rgbImage = (RGBImage) o; if (height != rgbImage.height) return false; if (width != rgbImage.width) return false; if (!Arrays.equals(pixels, rgbImage.pixels)) return false; return true; } @Override public int hashCode() { int result = width; result = 31 * result + height; result = 31 * result + Arrays.hashCode(pixels); return result; } public String getHex(int x, int y) { return getPixel(x, y).getHexString(); } public RGBImage clip(int x, int y, int width, int height) { return clip(new Rectangle(x, y, width, height)); } public RGBImage clipLine(int y) { return clip(0, y, width, 1); } public int numPixels() { return width*height; } } static boolean equals(Object a, Object b) { return a == null ? b == null : a.equals(b); } static boolean inRange(int x, int n) { return x >= 0 && x < n; } static int asInt(Object o) { return toInt(o); } static int hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static int toInt(Object o) { if (o == null) return 0; if (o instanceof Number) return ((Number) o).intValue(); if (o instanceof String) return parseInt((String) o); throw fail("woot not int: " + getClassName(o)); } static int toInt(long l) { if (l != (int) l) throw fail("Too large for int: " + l); return (int) l; } }