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 String dataProgID = "#1002143"; // use data dir of "These Are Bot" v1 (don't use both bots at once...) // TODO: make a PersistentMultiMap? static MultiMap lists = new MultiMap(); static MultiMap notLists = new MultiMap(); public static void main(String[] args) throws Exception { load(dataProgID, "lists"); load(dataProgID, "notLists"); makeBot("These Are Bot."); } synchronized static String answer(String s) { Matches m = new Matches(); List tok = javaTok(s); if (eqic(get(tok, 1), "these") && eqic(get(tok, 3), "are") && eqic(get(tok, 7), ":")) { String kind = unquote(get(tok, 5)); List rest = subList(tok, 8); return addThem(kind, rest, true); } if (eqic(get(tok, 1), "these") && eqic(get(tok, 3), "are") && eqic(get(tok, 5), "not") && eqic(get(tok, 9), ":")) { String kind = unquote(get(tok, 7)); List rest = subList(tok, 10); return addThem(kind, rest, false); } if (match("name a *", s, m)) { String kind = singular(simplify(m.unq(0))); List items = lists.get(kind); if (empty(items)) return null; //"I don't know any " + m.unq(0); return randomOne(items); } if (match("name * *", s, m)) { String n = m.get(0); if (isInteger(n)) { int _n = min(100, parseInt(n)); String kind = singular(simplify(m.unq(1))); List items = cloneList(lists.get(kind)); if (l(items) < _n) return "I don't know that many " + m.unq(1); List l = new ArrayList(); for (int i = 0; i < _n; i++) { String x = randomOne(items); l.add(x); items.remove(x); } return join(", ", l); } } String userName = (String) ( callOpt(mainBot, "getUserName")); if (match("am i a *", s, m)) { if (empty(userName)) return "Well I don't know who you are! Please join slack.com"; String kind = m.unq(0); boolean result = containsIgnoreCase(lists.get(kind), userName) || containsIgnoreCase(lists.get(kind), dropPrefix("@", userName)); if (result) return format("Yes, you are a *!", kind); else return format("As far as I know, you are not a *.", kind); } if (match("is * a *", s, m)) { String instance = m.unq(0); String kind = singular(simplify(m.unq(1))); boolean result = containsIgnoreCase(lists.get(kind), instance); if (result) return format("Yes, * is a *", instance, kind); else return null; //ret format("I don't know that * is a *", instance, kind); } if (match("Are there any * other than *?", s, m)) { String kind = singular(simplify(m.unq(0))); String instance = m.unq(1); List items = cloneList(lists.get(kind)); removeIgnoreCase(items, instance); if (empty(items)) return format("No, * is the only *.", instance, kind); else return format("Yes, for example *", randomOne(items)); } if (match("list all *", s, m)) { String kind = singular(simplify(m.unq(0))); List items = lists.get(kind); if (empty(items)) return null; // format("I don't know any *", kind); else return join(", ", items); } if (match("list all ex *", s, m)) { String kind = singular(simplify(m.unq(0))); List items = notLists.get(kind); if (empty(items)) return null; // format("I don't know any ex *", kind); else return join(", ", items); } return null; } static String simplify(String s) { return toLower(s); } static String singular(String s) { if (s.endsWith("ies")) return dropSuffix("ies", s) + "y"; return dropSuffix("s", s); } static String addThem(String kind, List rest, boolean plus) { kind = singular(simplify(kind)); boolean comma = rest.contains(","); List items = new ArrayList(); if (comma) for (List item : splitListBy(rest, ",")) items.add(trim(join(simpleSpaces(item)))); else items.addAll(codeTokensOnly(rest)); printFormat((plus ? "Adding" : "Removing") + " items * to/from kind *", items, kind); (plus ? lists : notLists).addAllIfNotThere(kind, items); (plus ? notLists : lists).removeAll(kind, items); save(dataProgID, "lists"); save(dataProgID, "notLists"); return format("OK, now " + l(lists.get(kind)) + " items in kind *", kind); } 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; } // replacement for class JavaTok // maybe incomplete, might want to add floating point numbers // todo also: extended multi-line strings static List javaTok(String s) { List tok = new ArrayList(); int l = s.length(); int i = 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; } tok.add(s.substring(i, j)); 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; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static List javaTok(List tok) { return javaTok(join(tok)); } static void save(String varName) { saveLocally(varName); } static void save(String progID, String varName) { saveLocally(progID, varName); } 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 A randomOne(List l) { if (empty(l)) return null; return l.get(random(l(l))); } static boolean isInteger(String s) { return s != null && Pattern.matches("\\-?\\d+", s); } 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 void load(String varName) { readLocally(varName); } static void load(String progID, String varName) { readLocally(progID, varName); } static String trim(String s) { return s == null ? null : s.trim(); } static String trim(StringBuilder buf) { return buf.toString().trim(); } static String trim(StringBuffer buf) { return buf.toString().trim(); } // 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()); } static List> splitListBy(List l, String sep) { int i = 0; List> x = new ArrayList>(); while (i < l(l)) { int j = indexOf(l, sep, i); if (j < 0) j = l(l); if (j > i) x.add(l.subList(i, j)); i = j+1; } return x; } static int makeBot(String greeting) { return makeAndroid3(greeting).port; } static void makeBot(Android3 a) { makeAndroid3(a); } 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); } public static String unquote(String s) { 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 String dropPrefix(String prefix, String s) { return s.startsWith(prefix) ? s.substring(l(prefix)) : s; } static void removeIgnoreCase(List l, String s) { int i = indexOfIgnoreCase(l, s); if (i >= 0) l.remove(i); } // Convert all spacing to a single space, so everything's neat & on one line. static List simpleSpaces(List tok) { tok = cloneList(tok); for (int i = 0; i < l(tok); i += 2) tok.set(i, tok.get(i).equals("") ? "" : " "); return tok; } static String simpleSpaces(String s) { return join(simpleSpaces(nlTok(s))); } 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 String toLower(String s) { return s.toLowerCase(); } static char toLower(char c) { return Character.toLowerCase(c); } static boolean match(String pat, String s) { return match3(pat, s); } static boolean match(String pat, String s, Matches matches) { return match3(pat, s, matches); } static String format(String pat, Object... args) { return format3(pat, args); } static String dropSuffix(String suffix, String s) { return s.endsWith(suffix) ? s.substring(0, l(s)-l(suffix)) : s; } 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; } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } static List cloneList(List l) { //O mutex = getOpt(l, "mutex"); /*if (mutex != null) synchronized(mutex) { ret new ArrayList(l); } else ret new ArrayList(l);*/ // assume mutex is equal to collection, which will be true unless you explicitly pass a mutex to synchronizedList() which no one ever does. synchronized(l) { return new ArrayList(l); } } static void printFormat(String s, Object... args) { print(format(s, args)); } static boolean containsIgnoreCase(List l, String s) { for (String x : l) if (eqic(x, s)) return true; return false; } static boolean containsIgnoreCase(String[] l, String s) { for (String x : l) if (eqic(x, s)) return true; return false; } static int l(Object[] array) { return array == null ? 0 : array.length; } static int l(byte[] array) { return array == null ? 0 : array.length; } static int l(int[] array) { return array == null ? 0 : array.length; } static int l(char[] array) { return array == null ? 0 : array.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(String s) { return s == null ? 0 : s.length(); } static int parseInt(String s) { return Integer.parseInt(s); } // class Matches is added by #752 static boolean match3(String pat, String s) { return match3(pat, s, null); } static boolean match3(String pat, String s, Matches matches) { if (s == null) return false; return match3(pat, parse3(s), matches); } static boolean match3(String pat, List toks, Matches matches) { List tokpat = parse3(pat); return match3(tokpat,toks,matches); } static boolean match3(List tokpat, List toks, Matches matches) { String[] m = match2(tokpat, toks); //print(structure(tokpat) + " on " + structure(toks) + " => " + structure(m)); if (m == null) return false; else { if (matches != null) matches.m = m; return true; } } static String format3(String pat, Object... args) { if (args.length == 0) return pat; List tok = javaTokPlusPeriod(pat); int argidx = 0; for (int i = 1; i < tok.size(); i += 2) if (tok.get(i).equals("*")) tok.set(i, format3_formatArg(argidx < args.length ? args[argidx++] : "null")); return join(tok); } static String format3_formatArg(Object arg) { if (arg == null) return "null"; if (arg instanceof String) { String s = (String) arg; return isIdentifier(s) || isNonNegativeInteger(s) ? s : quote(s); } if (arg instanceof Integer || arg instanceof Long) return String.valueOf(arg); return quote(structure(arg)); } static class Android3 { String greeting; boolean publicOverride; // optionally set this in client int startPort = 5000; // optionally set this in client Responder responder; boolean console = true; boolean daemon = false; boolean incomingSilent = false; boolean useMultiPort = true; // set by system int port; long vport; DialogHandler handler; ServerSocket server; Android3(String greeting) { this.greeting = greeting;} Android3() {} synchronized void dispose() { if (server == null) return; try { server.close(); } catch (IOException e) { print("[internal] " + e); } server = null; } } static abstract class Responder { abstract String answer(String s, List history); } static Android3 makeAndroid3(final String greeting) { return makeAndroid3(new Android3(greeting)); } static Android3 makeAndroid3(final String greeting, Responder responder) { Android3 android = new Android3(greeting); android.responder = responder; return makeAndroid3(android); } static Android3 makeAndroid3(final Android3 a) { if (a.responder == null) a.responder = new Responder() { String answer(String s, List history) { return callStaticAnswerMethod(s, history); } }; print(a.greeting); if (a.useMultiPort) { a.vport = addToMultiPort(a.greeting, a.responder); if (a.vport == 1) makeAndroid3_handleConsole(a); return a; } a.handler = makeAndroid3_makeDialogHandler(a); a.port = a.daemon ? startDialogServerOnPortAboveDaemon(a.startPort, a.handler) : startDialogServerOnPortAbove(a.startPort, a.handler); a.server = startDialogServer_serverSocket; if (a.console && makeAndroid3_consoleInUse()) a.console = false; if (a.console) makeAndroid3_handleConsole(a); record(a); return a; } static void makeAndroid3_handleConsole(final Android3 a) { // Console handling stuff print("You may also type on this console."); Thread _t_0 = new Thread() { public void run() { try { List history = new ArrayList(); String line; while ((line = readLine()) != null) { if (line == "bye") { print("> bye stranger"); history = new ArrayList(); } else { history.add(line); history.add(makeAndroid3_getAnswer(line, history, a)); // prints answer on console too } } } catch (Exception _e) { throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } } }; _t_0.start(); } static DialogHandler makeAndroid3_makeDialogHandler(final Android3 a) { return new DialogHandler() { public void run(final DialogIO io) { if (!a.publicOverride && !(publicCommOn() || io.isLocalConnection())) { io.sendLine("Sorry, not allowed"); return; } String dialogID = randomID(8); io.sendLine(a.greeting + " / Your ID: " + dialogID); List history = new ArrayList(); while (io.isStillConnected()) { if (io.waitForLine()) { final String line = io.readLineNoBlock(); String s = dialogID + " at " + now() + ": " + quote(line); if (!a.incomingSilent) print(s); if (line == "bye") { io.sendLine("bye stranger"); return; } Matches m = new Matches(); history.add(line); String answer; if (match3("this is a continuation of talk *", s, m) || match3("hello bot! this is a continuation of talk *", s, m)) { dialogID = unquote(m.m[0]); answer = "ok"; } else answer = makeAndroid3_getAnswer(line, history, a); history.add(answer); io.sendLine(answer); //appendToLog(logFile, s); } } }}; } static String makeAndroid3_getAnswer(String line, List history, Android3 a) { String answer; try { answer = makeAndroid3_fallback(line, history, a.responder.answer(line, history)); } catch (Throwable e) { e = getInnerException(e); e.printStackTrace(); answer = e.toString(); } if (!a.incomingSilent) print("> " + shorten(answer, 500)); return answer; } static String makeAndroid3_fallback(String s, List history, String answer) { // Now we only do the safe thing instead of VM inspection - give out our process ID if (answer == null && match3("what is your pid", s)) return getPID(); if (answer == null && match3("what is your program id", s)) // should be fairly safe, right? return getProgramID(); if (match3("get injection id", s)) return getInjectionID(); if (answer == null) answer = "?"; if (answer.indexOf('\n') >= 0 || answer.indexOf('\r') >= 0) answer = quote(answer); return answer; } static boolean makeAndroid3_consoleInUse() { for (Object o : record_list) if (o instanceof Android3 && ((Android3) o).console) return true; return false; } static int random(int n) { return new Random().nextInt(n); } static ArrayList litlist(A... a) { return new ArrayList(Arrays.asList(a)); } static void readLocally(String varNames) { readLocally(programID(), varNames); } // read a string variable from standard storage // does not overwrite variable contents if there is no file static synchronized void readLocally(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(main.class, variableName, unstructure(value)); } } } static void readLocally_set(Class 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 int indexOf(List l, A a, int startIndex) { for (int i = startIndex; i < l(l); i++) if (eq(l.get(i), a)) return i; return -1; } // 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 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 boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(String s) { return s == null || s.length() == 0; } static void saveLocally(String variableName) { saveLocally(programID(), variableName); } static synchronized void saveLocally(String progID, String variableName) { File textFile = new File(programDir(progID), variableName + ".text"); File structureFile = new File(programDir(progID), variableName + ".structure"); Object x = get(main.class, variableName); if (x == null) { textFile.delete(); structureFile.delete(); } else if (x instanceof String) { structureFile.delete(); saveTextFile(textFile, (String) x); } else { textFile.delete(); saveTextFile(structureFile, structure(x)); } } // 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 List nlTok(String s) { return javaTokPlusPeriod(s); } 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 void print() { print(""); } static void print(Object o) { String s = String.valueOf(o) + "\n"; 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); } static void print(long l) { print(String.valueOf(l)); } 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); } } } // works on lists and strings and null static int indexOfIgnoreCase(Object a, Object b) { if (a == null) return -1; if (a instanceof String) { Matcher m = Pattern.compile((String) b, Pattern.CASE_INSENSITIVE + Pattern.LITERAL).matcher((String) a); if (m.find()) return m.start(); else return -1; } if (a instanceof List) { for (int i = 0; i < ((List) a).size(); i++) { Object o = ((List) a).get(i); if (o != null && ((String) o).equalsIgnoreCase((String) b)) return i; } return -1; } throw fail("Unknown type: " + a); } // match2 matches multiple "*" (matches a single token) wildcards and zero or one "..." wildcards (matches multiple tokens) static String[] match2(List pat, List tok) { // standard case (no ...) int i = pat.indexOf("..."); if (i < 0) return match2_match(pat, tok); pat = new ArrayList(pat); // We're modifying it, so copy first pat.set(i, "*"); while (pat.size() < tok.size()) { pat.add(i, "*"); pat.add(i+1, ""); // doesn't matter } return match2_match(pat, tok); } static String[] match2_match(List pat, List tok) { List result = new ArrayList(); if (pat.size() != tok.size()) { /*if (debug) print("Size mismatch: " + structure(pat) + " vs " + structure(tok));*/ return null; } for (int i = 1; i < pat.size(); i += 2) { String p = pat.get(i), t = tok.get(i); /*if (debug) print("Checking " + p + " against " + t);*/ if (eq(p, "*")) result.add(t); else if (!equalsIgnoreCase(unquote(p), unquote(t))) // bold change - match quoted and unquoted now return null; } return result.toArray(new String[result.size()]); } static boolean isIdentifier(String s) { return isJavaIdentifier(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 shorten(String s, int max) { if (s == null) return ""; return s.length() <= max ? s : s.substring(0, Math.min(s.length(), max)) + "..."; } static boolean publicCommOn() { return "1".equals(loadTextFile(new File(userHome(), ".javax/public-communication"))); } static List record_list = synchroList(); static void record(Object o) { record_list.add(o); } static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { return new File(javaxDataDir(), formatSnippetID(snippetID)); } static boolean isNonNegativeInteger(String s) { return s != null && Pattern.matches("\\d+", s); } static String getInjectionID() { return (String) call(getJavaX(), "getInjectionID", getMainClass()); } 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 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)); } static RuntimeException fail(String msg, Object... args) { throw new RuntimeException(format(msg, args)); } /** 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"; if (contents != null) { FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); 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 (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); } public static void saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); } catch (IOException e) { throw new RuntimeException(e); } } // This is made for NL parsing. // It's javaTok extended with "..." token, "$n" and "#n" and // special quotes (which are converted to normal ones). static List javaTokPlusPeriod(String s) { List tok = new ArrayList(); int l = s.length(); int i = 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; } tok.add(s.substring(i, j)); i = j; if (i >= l) break; c = s.charAt(i); cc = s.substring(i, Math.min(i+2, l)); // scan for non-whitespace if (c == '\u201C' || c == '\u201D') c = '"'; // normalize quotes if (c == '\'' || c == '"') { char opener = c; ++j; while (j < l) { char _c = s.charAt(j); if (_c == '\u201C' || _c == '\u201D') _c = '"'; // normalize quotes if (_c == opener) { ++j; break; } else if (s.charAt(j) == '\\' && j+1 < l) j += 2; else ++j; } if (j-1 >= i+1) { tok.add(opener + s.substring(i+1, j-1) + opener); i = j; continue; } } else if (Character.isJavaIdentifierStart(c)) do ++j; while (j < l && (Character.isJavaIdentifierPart(s.charAt(j)) || s.charAt(j) == '\'')); // for things like "this one's" else if (Character.isDigit(c)) do ++j; while (j < l && Character.isDigit(s.charAt(j))); 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 if (s.substring(j, Math.min(j+3, l)).equals("...")) j += 3; else if (c == '$' || c == '#') do ++j; while (j < l && Character.isDigit(s.charAt(j))); else ++j; tok.add(s.substring(i, j)); i = j; } if ((tok.size() % 2) == 0) tok.add(""); return tok; } static Object addToMultiPort_responder; static long addToMultiPort(final String botName) { return addToMultiPort(botName, new Object() { public String answer(String s, List history) { String answer = (String) ( callOpt(getMainClass(), "answer", s, history)); if (answer != null) return answer; answer = (String) callOpt(getMainClass(), "answer", s); if (answer != null) return answer; if (match3("get injection id", s)) return getInjectionID(); return null; } }); } static long addToMultiPort(final String botName, final Object responder) { print(botName); addToMultiPort_responder = responder; startMultiPort(); List ports = getMultiPorts(); if (ports.isEmpty()) fail("No multiports!"); if (ports.size() > 1) print("Multiple multi-ports. Using last one."); Object port = last(ports); Object responder2 = new Object() { public String answer(String s, List history) { if (match3("get injection id", s)) return getInjectionID(); if (match3("your name", s)) return botName; return (String) call(responder, "answer", s, history); } }; record(responder2); return (Long) call(port, "addResponder", botName, responder2); } static String programID; static String getProgramID() { return programID; } // TODO: ask JavaX instead static String getProgramID(Class c) { return or((String) getOpt(c, "programID"), "?"); } static String getProgramID(Object o) { return getProgramID(getMainClass(o)); } static List parse3(String s) { return dropPunctuation(javaTokPlusPeriod(s)); } static class DynamicObject { String className; Map fieldValues = new TreeMap(); } static Object unstructure(String text) { return unstructure(text, false); } // actually it's now almost the same as jsonDecode :) static Object unstructure(String text, final boolean allDynamic) { final List tok = javaTok(text); class X { int i = 1; Object parse() { String t = tok.get(i); if (t.startsWith("\"")) { String s = unquote(tok.get(i)); i += 2; return s; } 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("class")) return parseClass(); if (t.equals("bigint")) return parseBigInt(); if (t.equals("d")) return parseDouble(); if (t.equals("l")) return parseLisp(); if (t.equals("null")) { i += 2; return null; } if (t.equals("false")) { i += 2; return false; } if (t.equals("true")) { i += 2; return true; } if (t.equals("-")) { t = tok.get(i+2); i += 4; t = dropSuffix("L", t); long l = -Long.parseLong(t); return l == (int) l ? (int) l : l; } if (isInteger(t) || isLongConstant(t)) { i += 2; t = dropSuffix("L", t); long l = Long.parseLong(t); return l == (int) l ? (int) l : l; } if (isJavaIdentifier(t)) { Class c = allDynamic ? null : findClass(t); DynamicObject dO = null; Object o = null; if (c != null) o = nuObject(c); else { dO = new DynamicObject(); dO.className = t; } 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 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 = unstructure(tok.get(i)); i += 2; 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()))); fail(quote(s) + " expected: " + prevToken + " " + nextTokens + " (" + i + "/" + tok.size() + ")"); } i += 2; } } return new X().parse(); } static AtomicInteger dialogServer_clients = new AtomicInteger(); static boolean dialogServer_printConnects; static Set dialogServer_knownClients = synchroTreeSet(); static int startDialogServerOnPortAbove(int port, DialogHandler handler) { while (!startDialogServerIfPortAvailable(port, handler)) ++port; return port; } static int startDialogServerOnPortAboveDaemon(int port, DialogHandler handler) { while (!startDialogServerIfPortAvailable(port, handler, true)) ++port; return port; } static void startDialogServer(int port, DialogHandler handler) { if (!startDialogServerIfPortAvailable(port, handler)) fail("Can't start dialog server on port " + port); } static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler) { return startDialogServerIfPortAvailable(port, handler, false); } static ServerSocket startDialogServer_serverSocket; static boolean startDialogServerIfPortAvailable(int port, final DialogHandler handler, boolean daemon) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port); } catch (IOException e) { // probably the port number is used - let's assume there already is a chat server. return false; } final ServerSocket _serverSocket = serverSocket; startDialogServer_serverSocket = serverSocket; Thread thread = new Thread("Socket accept port " + port) { public void run() { try { while (true) { try { final Socket s = _serverSocket.accept(); String client = s.getInetAddress().toString(); if (!dialogServer_knownClients.contains(client)) { print("connect from " + client + " - clients: " + dialogServer_clients.incrementAndGet()); dialogServer_knownClients.add(client); } String threadName = "Handling client " + s.getInetAddress(); Thread t2 = new Thread(threadName) { public void run() { try { final Writer w = new OutputStreamWriter(s.getOutputStream(), "UTF-8"); final BufferedReader in = new BufferedReader( new InputStreamReader(s.getInputStream(), "UTF-8")); DialogIO io = new DialogIO() { // This should be the same as #1001076 (talkTo) boolean isLocalConnection() { return s.getInetAddress().isLoopbackAddress(); } boolean isStillConnected() { return !(eos || s.isClosed()); } void sendLine(String line) { try { w.write(line + "\n"); w.flush(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} String readLineImpl() { try { return in.readLine(); } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} void close() { try { s.close(); } catch (IOException e) { // whatever } } Socket getSocket() { return s; } }; try { handler.run(io); } finally { s.close(); } } catch (IOException e) { print("[internal] " + e); } finally { //print("client disconnect - " + dialogServer_clients.decrementAndGet() + " remaining"); } } }; // Thread t2 t2.setDaemon(true); // ? t2.start(); } catch (SocketTimeoutException e) { } } } catch (IOException e) { print("[internal] " + e); } }}; if (daemon) thread.setDaemon(true); thread.start(); print("Dialog server on port " + port + " started."); return true; } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } static List synchroList() { return Collections.synchronizedList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } // try to get our current process ID static String getPID() { String name = ManagementFactory.getRuntimeMXBean().getName(); return name.replaceAll("@.*", ""); } 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_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static Field set_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static String callStaticAnswerMethod(List bots, String s) { for (Class c : bots) try { String answer = callStaticAnswerMethod(c, s); if (!empty(answer)) return answer; } catch (Throwable e) { print("Error calling " + getProgramID(c)); e.printStackTrace(); } return null; } static String callStaticAnswerMethod(Class c, String s) { String answer = (String) callOpt(c, "answer", s, litlist(s)); if (answer == null) answer = (String) callOpt(c, "answer", s); return emptyToNull(answer); } static String callStaticAnswerMethod(String s, List history) { String answer = (String) callOpt(getMainClass(), "answer", s, history); if (answer == null) answer = (String) callOpt(getMainClass(), "answer", s); return emptyToNull(answer); } static BufferedReader readLine_reader; static String readLine() { return (String) call(getJavaX(), "readLine"); } static Throwable getInnerException(Throwable e) { while (e.getCause() != null) e = e.getCause(); return e; } // "seen" is now default static String structure(Object o) { return structure(o, 0, new IdentityHashMap()); } static String structure_seen(Object o) { return structure(o, 0, new IdentityHashMap()); } // leave to false, unless unstructure() breaks static boolean structure_allowShortening = false; static String structure(Object o, int stringSizeLimit, IdentityHashMap seen) { 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 Long) return o + "L"; if (seen != null) { Integer ref = seen.get(o); if (ref != null) return "r" + ref; seen.put(o, seen.size()+1); } String name = o.getClass().getName(); StringBuilder buf = new StringBuilder(); if (o instanceof HashSet) return "hashset" + structure(new ArrayList((Set) o), stringSizeLimit, seen); if (o instanceof TreeSet) return "treeset" + structure(new ArrayList((Set) o), stringSizeLimit, seen); if (o instanceof Collection) { for (Object x : (Collection) o) { if (buf.length() != 0) buf.append(", "); buf.append(structure(x, stringSizeLimit, seen)); } return "[" + buf + "]"; } if (o instanceof Map) { for (Object e : ((Map) o).entrySet()) { if (buf.length() != 0) buf.append(", "); buf.append(structure(((Map.Entry) e).getKey(), stringSizeLimit, seen)); buf.append("="); buf.append(structure(((Map.Entry) e).getValue(), stringSizeLimit, seen)); } return (o instanceof HashMap ? "hashmap" : "") + "{" + buf + "}"; } if (o.getClass().isArray()) { int n = Array.getLength(o); for (int i = 0; i < n; i++) { if (buf.length() != 0) buf.append(", "); buf.append(structure(Array.get(o, i), stringSizeLimit, seen)); } return "array{" + buf + "}"; } if (o instanceof Class) return "class(" + quote(((Class) o).getName()) + ")"; if (o instanceof Throwable) return "exception(" + quote(((Throwable) o).getMessage()) + ")"; // Need more cases? This should cover all library classes... if (name.startsWith("java.") || name.startsWith("javax.")) return String.valueOf(o); String shortName = o.getClass().getName().replaceAll("^main\\$", ""); if (shortName.equals("Lisp")) { buf.append("l(" + structure(getOpt(o, "head"), stringSizeLimit, seen)); 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(arg, stringSizeLimit, seen)); } buf.append(")"); return str(buf); } int numFields = 0; String fieldName = ""; if (shortName.equals("DynamicObject")) { shortName = (String) get(o, "className"); Map fieldValues = (Map) get(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(value, stringSizeLimit, seen)); } ++numFields; } } else { // regular class // TODO: go to superclasses too Field[] fields = o.getClass().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(value, stringSizeLimit, seen)); } ++numFields; } } 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 s; } static String randomID(int length) { return makeRandomID(length); } static boolean equalsIgnoreCase(String a, String b) { return a == null ? b == null : a.equalsIgnoreCase(b); } // 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 str(Object o) { return String.valueOf(o); } 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; } static Set synchroTreeSet() { return Collections.synchronizedSet(new TreeSet()); } static String unnull(String s) { return s == null ? "" : s; } static List unnull(List l) { return l == null ? emptyList() : l; } 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 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 __javax; static Class getJavaX() { return __javax; } 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 boolean isTrue(Object o) { return booleanValue(o); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } static A last(List l) { return l.isEmpty() ? null : l.get(l.size()-1); } static List getMultiPorts() { return (List) call(getJavaX(), "getMultiPorts"); } static String emptyToNull(String s) { return eq(s, "") ? null : s; } 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(Class c, Object... args) { try { Constructor m = nuObject_findConstructor(c, args); m.setAccessible(true); return 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 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); } static A or(A a, A b) { return a != null ? a : b; } static List dropPunctuation_keep = litlist("*", "<", ">"); static List dropPunctuation(List tok) { tok = new ArrayList(tok); for (int i = 1; i < tok.size(); i += 2) { String t = tok.get(i); if (t.length() == 1 && !Character.isLetter(t.charAt(0)) && !Character.isDigit(t.charAt(0)) && !dropPunctuation_keep.contains(t)) { tok.set(i-1, tok.get(i-1) + tok.get(i+1)); tok.remove(i); tok.remove(i); i -= 2; } } return tok; } static String dropPunctuation(String s) { return join(dropPunctuation(nlTok(s))); } static boolean nempty(Collection c) { return !isEmpty(c); } static boolean nempty(String s) { return !isEmpty(s); } static String makeRandomID(int length) { Random random = new Random(); char[] id = new char[length]; for (int i = 0; i < id.length; i++) id[i] = (char) ((int) 'a' + random.nextInt(26)); return new String(id); } 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 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_findField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field)) return f; return null; } static Field setOpt_findStaticField(Class c, String field) { for (Field f : c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0) return f; return null; } 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 call(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 { 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; } // start multi-port if none exists in current VM. static void startMultiPort() { if (getMultiPorts().isEmpty()) callMain(hotwire("#1001672")); } static Object getBot(String botID) { return call(getMainBot(), "getBot", botID); } static void callMain(Object c, String... args) { callOpt(c, "main", new Object[] {args}); } static boolean booleanValue(Object o) { return eq(true, o); } static List emptyList() { return Collections.emptyList(); } public static long parseSnippetID(String snippetID) { long id = Long.parseLong(shortenSnippetID(snippetID)); if (id == 0) fail("0 is not a snippet ID"); return id; } // compile JavaX source, load classes & return main class // src can be a snippet ID or actual source code // TODO: record injection? static Class hotwire(String src) { try { Class j = getJavaX(); synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); if (androidContext != null) return (Class) call(j, "loadx2android", srcDir, src); File classesDir = (File) call(j, "TempDirMaker_make"); String javacOutput = (String) call(j, "compileJava", srcDir, libraries, classesDir); System.out.println(javacOutput); URL[] urls = new URL[libraries.size()+1]; urls[0] = classesDir.toURI().toURL(); for (int i = 0; i < libraries.size(); i++) urls[i+1] = libraries.get(i).toURI().toURL(); // make class loader URLClassLoader classLoader = new URLClassLoader(urls); // load & return main class Class theClass = classLoader.loadClass("main"); callOpt(j, "registerSourceCode", theClass, loadTextFile(new File(srcDir, "main.java"))); call(j, "setVars", theClass, isSnippetID(src) ? src: null); if (isSnippetID(src)) callOpt(j, "addInstance", src, theClass); return theClass; } } catch (Exception e) { throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); } } static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; } 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 Object mainBot; static Object getMainBot() { return mainBot; } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } static long parseLong(String s) { return Long.parseLong(s); } static abstract class DialogIO { String line; boolean eos; abstract String readLineImpl(); abstract boolean isStillConnected(); abstract void sendLine(String line); abstract boolean isLocalConnection(); abstract Socket getSocket(); abstract void close(); int getPort() { return getSocket().getPort(); } boolean helloRead; String readLineNoBlock() { String l = line; line = null; return l; } boolean waitForLine() { try { if (line != null) return true; //print("Readline"); line = readLineImpl(); //print("Readline done: " + line); if (line == null) eos = true; return line != null; } catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }} String readLine() { waitForLine(); helloRead = true; return readLineNoBlock(); } String ask(String s, Object... args) { if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); sendLine(s); return readLine(); } String askLoudly(String s, Object... args) { if (!helloRead) readLine(); if (args.length != 0) s = format3(s, args); print("> " + s); sendLine(s); String answer = readLine(); print("< " + answer); return answer; } void pushback(String l) { if (line != null) fail(); line = l; helloRead = false; } } static abstract class DialogHandler { abstract void run(DialogIO io); } static class Matches { String[] m; String get(int i) { return m[i]; } String unq(int i) { return unquote(m[i]); } String fsi(int i) { return formatSnippetID(unq(i)); } boolean bool(int i) { return "true".equals(unq(i)); } String rest() { return m[m.length-1]; } // for matchStart int psi(int i) { return Integer.parseInt(unq(i)); } } static class MultiMap { Map> data = new HashMap>(); MultiMap() {} MultiMap(MultiMap map) { putAll(map); } public void put(A key, B value) { List list = data.get(key); if (list == null) data.put(key, list = new ArrayList()); list.add(value); } public void addAll(A key, Collection values) { putAll(key, values); } public void addAllIfNotThere(A key, Collection values) { for (B value : values) setPut(key, value); } void setPut(A key, B value) { if (!containsPair(key, value)) put(key, value); } boolean containsPair(A key, B value) { return get(key).contains(value); } public void putAll(A key, Collection values) { for (B value : values) put(key, value); } void removeAll(A key, Collection values) { for (B value : values) remove(key, value); } public List get(A key) { List list = data.get(key); return list == null ? Collections. emptyList() : list; } // returns actual mutable live list // creates the list if not there public List getActual(A key) { List list = data.get(key); if (list == null) data.put(key, list = litlist()); return list; } void clean(A key) { List list = data.get(key); if (list != null && list.isEmpty()) data.remove(key); } public Set keySet() { return data.keySet(); } public Set keys() { return data.keySet(); } public void remove(A key) { data.remove(key); } public void remove(A key, B value) { List list = data.get(key); if (list != null) { list.remove(value); if (list.isEmpty()) data.remove(key); } } public void clear() { data.clear(); } public boolean containsKey(A key) { return data.containsKey(key); } public B getFirst(A key) { List list = get(key); return list.isEmpty() ? null : list.get(0); } public void putAll(MultiMap map) { for (A key : map.keySet()) putAll(key, map.get(key)); } // note: expensive operation int size() { int n = 0; for (List l : data.values()) n += l(l); return n; } // expensive operation List reverseGet(B b) { List l = new ArrayList(); for (A key : data.keySet()) if (data.get(key).contains(b)) l.add(key); return l; } } static Set keys(Map map) { return map.keySet(); } static Set keys(Object map) { return keys((Map) map); } }